JAXB(Java Architecture for XML Binding) 是一个业界的标准,是一项可以根据XML Schema产生Java类的技术。 该过程中,JAXB也提供了将XML实例文档反向生成Java对象树的方法,并能将Java对象树的内容重新写到XML实例文档。 从另一方面来讲,JAXB提供了快速而简便的方法将XML模式绑定到Java表示,从而使得Java开发者在Java应用程序中能方便地结合XML数据和处理函数。
样例:
Person.class @XmlRootElement public class Person { //主键 private int id; //姓名 private String name; //性别 private String sex; //地址 private String address; public int getId() { return id; } @XmlElement public void setId(int id) { this.id = id; } public String getName() { return name; } @XmlElement public void setName(String name) { this.name = name; } public String getSex() { return sex; } @XmlElement public void setSex(String sex) { this.sex = sex; } public String getAddress() { return address; } @XmlElement public void setAddress(String address) { this.address = address; } @Override public String toString() { return "Person{" + "id=" + id + ", name='" + name + '\'' + ", sex='" + sex + '\'' + ", address='" + address + '\'' + '}'; } public Person(int id, String name, String sex, String address) { this.id = id; this.name = name; this.sex = sex; this.address = address; } } Utils public class JAXBUtils { /** * xml 文件转 java对象 */ public static void generateBean() { File file = new File("E:\\person.xml"); JAXBContext jc = null; try { //根据Person类生成上下文对象 jc = JAXBContext.newInstance(Person.class); //从上下文中获取Unmarshaller对象,用作将xml解组(转换)为java 对象 Unmarshaller uma = jc.createUnmarshaller(); //解组 Person person = (Person) uma.unmarshal(file); System.out.println(person); } catch (JAXBException e) { e.printStackTrace(); } } /** * 对象转 xml 生成xml文件 */ public static void generateXML() { Person person = new Person(1, "张三", "男", "朝阳区"); File file = new File("E:\\person.xml"); JAXBContext jc = null; try { //根据Person类生成上下文对象 jc = JAXBContext.newInstance(Person.class); //从上下文中获取Marshaller对象,用作将bean编组(转换)为xml Marshaller ma = jc.createMarshaller(); //以下是为生成xml做的一些配置 //格式化输出,即按标签自动换行,否则就是一行输出 ma.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); //设置编码(默认编码就是utf-8) ma.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); //是否省略xml头信息,默认不省略(false) ma.setProperty(Marshaller.JAXB_FRAGMENT, false); //编组 ma.marshal(person, file); } catch (JAXBException e) { e.printStackTrace(); } } /** * 对象转 xml 字符串 */ public static String beanToXml(Class clazz, Object object) throws JAXBException { // 获取上下文对象 JAXBContext context = JAXBContext.newInstance(clazz); // 根据上下文获取marshaller对象 Marshaller marshaller = context.createMarshaller(); // 设置编码字符集 marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); // 格式化XML输出,有分行和缩进 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); // 去掉生成xml的默认报文头 marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true); ByteArrayOutputStream baos = new ByteArrayOutputStream(); marshaller.marshal(object, baos); return new String(baos.toByteArray()); } /** * xml 字符串 转 java对象 */ public static <T> T xmlToBean(Class<T> clazz,String xml) throws JAXBException{ File file = new File("E:\\person.xml"); //根据class生成上下文对象 JAXBContext jc = JAXBContext.newInstance(clazz); //从上下文中获取Unmarshaller对象,用作将xml解组(转换)为java 对象 Unmarshaller uma = jc.createUnmarshaller(); //解组 Object obj = uma.unmarshal(new ByteArrayInputStream(xml.getBytes())); return clazz.cast(obj); } }从jdk1.7开始,JAXB就对解组和编组的方法进行了更简单的封装,所以实际项目中除非自己要进行个性化设置,否则大可不用自己再创建JAXBContext实例,直接通过JAXB静态调用相应的工具方法就行了,于是上面的测试方法可以写的更简练些:
例:
public void generateXML() { Person person = new Person("abc", "男", "北京", "朝阳区"); File file = new File("E:\\person.xml"); JAXB.marshal(person, file); } public void generateBean() { File file = new File("E:\\person.xml"); Person person = JAXB.unmarshal(file, Person.class); System.out.println(person); }直接使用默认的配置,已经足够应付大多数情况。
作用
类级别的注解,将类映射为xml全局元素,也就是根元素。就像spring配置文件中的beans。上面的例子中我将该注解用在了person类上,生成了根元素。常与@XmlType,@XmlAccessorType,@XmlAccessorOrder连用。
属性
该注解含有name和namespace两个属性。namespace属性用于指定生成的元素所属的命名空间。name属性用于指定生成元素的名字,若不指定则默认使用类名小写作为元素名。
修改上面的例子,在该注解上使用name属性:
@XmlRootElement(name = "personBase") public class Person { private int id; private String name; ... ... //省略下面代码 } @XmlElement作用
字段,方法,参数级别的注解。该注解可以将被注解的字段(非静态),或者被注解的get/set方法对应的字段映射为本地元素,也就是子元素。默认使用字段名或get/set方法去掉前缀剩下部分小写作为元素名(在字段名和get/set方法符合命名规范的情况下)。 上面例子中,id、addr、name、gender、area都被映射成了元素的子元素。下文会配合@XmlAccessorType注解详细讲解该注解的用法。常与@XmlValue,@XmlJavaTypeAdapter,@XmlElementWrapper连用。
属性
该注解的属性常用的属性有有:name、nillable、required、namespace、defaultValue name属性可以指定生成元素的名字,同@XmlRootElement注解的name属性一样,不再举例。 nillable属性可以指定元素的文本值是否可以为空,默认为false。修改上面例子:
@XmlElement(nillable = true) public void setName(String name) { this.name = name;则生成的xsd(为了节省篇幅,只截取必要的片段)为:
<xs:element name="name" type="xs:string" nillable="true" minOccurs="0"/>required属性可以指定该元素是否必须出现,默认为false,所以在xsd中会有对应的属性minOccurs=“0”。修改该属性为true
@XmlElement(nillable = true, required = true) public void setName(String name) { this.name = name; }生成的xsd文件为:
<xs:element name="name" type="xs:string" nillable="true" minOccurs="1"/>namespace属性可以指定该元素所属的命名空间 defaultValue属性可以指定该元素默认的文本值
@XmlAttribute作用
字段和方法级别的注解。 该注解会将字段或get/set方法对应的字段映射成本类对应元素的属性,属性名默认使用字段名或get/set方法去掉前缀剩下部分首字母小写(在字段名和get/set方法符合命名规范的情况下)。
修改上面例子:
@XmlAttribute public void setSex(String sex) { this.sex = sex; }生成的xml:
<xs:element name="personBase"> <xs:complexType> <xs:sequence> <xs:element type="xs:string" name="address" minOccurs="0"/> <xs:element type="xs:int" name="id" minOccurs="0"/> <xs:element type="xs:string" name="name" minOccurs="0"/> </xs:sequence> <xs:attribute type="xs:string" name="sex"/> </xs:complexType> </xs:element>属性
该注解有name,required,namespace三个属性。用法和@XmlElement注解相同,不再举例。
@XmlTransient作用
类,字段,方法级别的注解。可使JAXB在映射xml元素时忽略被注解的类,字段,get/set对应字段。需要注意的是该注解与所有其他JAXB注释相互排斥,也就是说与其他注释连用就会报错。
修改上面例子:
@XmlTransient public void setId(int id) { this.id = id; }生成的xml:
属性
该注解没有属性。
@XmlAccessorType作用
包和类级别的注解。javaEE的API对该注解的解释是:控制字段是否被默认序列化。 通俗来讲,就是决定哪些字段或哪些get/set方法对应的字段会被映射为xml元素,需要注意的是字段或get/set方法的访问权限(public/private)会影响字段是否被映射为xml元素,下面会详细讲解。
属性
该注解只有一个value属性,可取的值是一个名为XmlAccessType的枚举类型里的值。
下面详细看一下这几个值分别有什么用:
XmlAccessType.PROPERTY
当使用了该值,只要字段有对应的get/set方法对(注意是成对出现,只有其中一个不会发生映射),不需要使用@XmlElement注解,不论该方法的访问权限是什么(即使是private),jaxb就会将该字段映射成xml元素。不过最好加上@XmlElement注解,get/set方法任选一个即可,都加上会报错。若在一个字段有set/get方法对但又在字段上添加@XmlElement注解会报属性重复的错误。若没有set/get方法对,则需要在字段上使用@XmlElement注解才可以映射为xml元素,否则不会发生映射。若get/set方法上使用了@XmlTransient注解,但想要对应字段发生映射,需要在对应字段上添加@XmlElement注解,此时不会报错,并将该字段映射为xml元素。XmlAccessType.FIELD
每个访问权限为public的字段,或者每个访问权限为public的get/set方法对,都会将字段映射为xml元素,即使不使用@XmlElement,但最好加上。不可同时存在public字段和对应的get/set方法对,不然会报属性重复的错误。若使用@XmlElement注解,需要注意只能在字段或get/set方法添加,两者任选其一,否则会报属性重复的错误。若字段不为public,get/set方法为public并使用了@XmlTransient,需要在字段上添加@XmlElement才会发生映射。若字段为public并使用了@XmlTransient,get/set方法对不为public,需要在get/set方法上使用@XmlElement才会映射。XmlAccessType.NONE
任何字段,get/set方法对都不会发生映射,除非使用某些注解,如@XmlElement,@XmlElementWrapper等。 @XmlAccessorOrder作用
包和类级别的注解。控制生成元素的顺序。
属性
只有一个value属性,可取的值是一个名为XmlAccessOrder的枚举类型的两个值:XmlAccessOrder.ALPHABETICAL 和XmlAccessOrder.UNDEFINED。 默认为XmlAccessOrder.UNDEFINED,代表按照类中字段的顺序生成元素的顺序。 另一个值则代表按照字母表的顺序对生成的元素排序。 但奇怪的是,只有jaxb按照field生成元素时,默认值才会生效,否则总是按照字母表的顺序排序。
@XmlElementWrapper作用
字段和方法级别的注解。围绕被映射的xml元素生成包装元素。主要用在集合对象映射后生成包装映射结果的xml元素。
修改上面的例子,添加一个Key类,在Person类中添加一个Key类的Set集合,修改如下:
Key.class
@XmlRootElement public class Key { private String roomNum; public Key() { } public Key(String roomNum) { this.roomNum = roomNum; } public String getRoomNum() { return roomNum; } @XmlElement public void setRoomNum(String roomNum) { this.roomNum = roomNum; } @Override public String toString() { return "Key{" + "roomNum='" + roomNum + '\'' + '}'; } }Person.class
@XmlRootElement(name = "personBase") @XmlAccessorType(XmlAccessType.PUBLIC_MEMBER) @XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL) public class Person { //主键 private int id; //姓名 private String name; //性别 private String sex; //地址 private String address; private List<Key> key; public int getId() { return id; } @XmlElement public void setId(int id) { this.id = id; } public String getName() { return name; } @XmlElement public void setName(String name) { this.name = name; } public String getSex() { return sex; } @XmlElement public void setSex(String sex) { this.sex = sex; } public String getAddress() { return address; } @XmlElement public void setAddress(String address) { this.address = address; } public List<Key> getKey() { return key; } @XmlElement public void setKey(List<Key> key) { this.key = key; } public Person(int id, String name, String sex, String address, List<Key> key) { this.id = id; this.name = name; this.sex = sex; this.address = address; this.key = key; } public Person() { } }生成的xml:
但同一元素应该需要被“包装”一下才显得有层次感,所以可以使用@XmlElementWrapper来实现:
@XmlElementWrapper(name = "keys") @XmlElement public void setKey(List<Key> key) { this.key = key; }生成的xml:
属性
该注解有name、nillable、namespace、required四个属性,用法同上,不再赘述。
@XmlJavaTypeAdapter作用
包、类、字段,方法、参数级别的注解。解决java日期(Date),数字(Number)格式化问题
直接看例子,修改Person类,添加一个Date类型字段:
@XmlRootElement(name = "personBase") @XmlAccessorType(XmlAccessType.PUBLIC_MEMBER) @XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL) public class Person { //...略 private Date date = new Date(); //...略 public Date getDate() { return date; } @XmlElement public void setDate(Date date) { this.date = date; } }生成的xml 这样的date格式显然令人不满意,我们需要例如“2018-05-20”这样的格式。这就需要@XmlJavaTypeAdapter注解,自定义一个适配器来解决这个问题。该注解的用法就是自定义适配器并继承XmlAdapter类,实现里面的marshal和unmarshal方法,并在该注解上引用。修改例子:
自定义的DateAdapter:
public class DateAdapter extends XmlAdapter<String, Date> { private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd"); @Override public Date unmarshal(String date) throws Exception { return SDF.parse(date); } @Override public String marshal(Date date) throws Exception { return SDF.format(date); } }Person.class
@XmlJavaTypeAdapter(DateAdapter.class) @XmlElement public void setDate(Date date) { this.date = date; }生成的xml 属性
常用的就是value属性,其他属性请自行研究。
