JAXB 深入显出 - JAXB 教程 简单XML生成

x33g5p2x  于2021-12-28 转载在 其他  
字(7.2k)|赞(0)|评价(0)|浏览(296)

摘要: JAXB 作为JDK的一部分,能便捷地将Java对象与XML进行相互转换,本教程从实际案例出发来讲解JAXB 2 的那些事儿。完整版目录
上一节主要是 One.java,本节每一小节都是不同的Java bean,展示不同的 Java 对象编组成 XML 。

改变XML Root Element name

默认的 XML Root Element name 是 Java 对象的 类名 首字母小写。

@Test public void test() throws JAXBException { // 首先创建 JAXBContext JAXBContext context = JAXBContext.newInstance(Two.class); // 初始化 Marshaller Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); // 创建简单的 Java bean Two o = new Two(); o.setName("Test two"); // 将结果输出到控制台 marshaller.marshal(o, System.out); }

其实最关键的就在这个 @XmlRootElement,指定name,然后最终的Root name就是这里指定的值。

@XmlRootElement(name = "Second") public class Two { private String name; // setters,getters }

输出结果就是上面指定的 name

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Second>
    <name>Test two</name>
</Second>

改变XML 子元素名称 Element name

在不指定确定值时,XML 子元素名称是Java 属性的名称。

@Test public void test3() throws JAXBException { JAXBContext context = JAXBContext.newInstance(Three.class); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); Three t = new Three(); t.setName("Test three"); marshaller.marshal(t, System.out); }

通过指定@XmlElementname,改变XML 元素名称。

@XmlRootElement public class Three { private String name; public String getName() { return name; } @XmlElement(name = "Naming") public void setName(String name) { this.name = name; } }

输出结果就是上面指定的 name

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<three>
    <Naming>Test three</Naming>
</three>

输出 XML 属性

默认情况下,Java 属性是XML 子元素。

@Test public void test4() throws JAXBException { JAXBContext context = JAXBContext.newInstance(Four.class); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); Four f = new Four(); f.setId("1004"); f.setName("Test three"); marshaller.marshal(f, System.out); }

通过指定@XmlAttribute 来限制输出为 XML 属性,同样地,可以指定 @XmlAttribute(name = "identity")改变XML 属性名称。

@XmlRootElement public class Four { private String id; private String name; public String getId() { return id; } @XmlAttribute public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }

输出结果id就是一个属性,没有指定name,所以还是输出为XML 元素。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<four id="1004">
    <name>Test three</name>
</four>

Java 对象嵌套

对于有层级结构的XML,可以通过Java bean嵌套来实现。

@Test public void test5() throws JAXBException { JAXBContext context = JAXBContext.newInstance(Five.class); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); Five f = new Five(); Four four = new Four(); four.setName("This is 4"); f.setName("Test 5"); f.setFour(four); marshaller.marshal(f, System.out); }

引用上一级的Four.java,就可以输出第二级 XML 结构。

@XmlRootElement public class Five { private String name; private Four four; // setters,getters }

输出的 XML 是有两级层级结构的,更复杂的结构将在接下来的章节中介绍。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<five>
    <four>
        <name>This is 4</name>
    </four>
    <name>Test 5</name>
</five>

使用构造器

在使用 Java 构造器构造Java对象,编组 XML 时,需要注意得手动设置空的构造器。

@Test public void test6() throws JAXBException { JAXBContext context = JAXBContext.newInstance(Six.class); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); Six six = new Six("1006", "Test6", "Some descrptions"); marshaller.marshal(six, System.out); }

如果要覆盖构造器,需要注意空构造器不能省。

@XmlRootElement(name = "Six") public class Six { private String code; private String name; private String desc; public Six() {} public Six(String code, String name, String desc) { super(); this.code = code; this.name = name; this.desc = desc; } // setters,getters }

生成的XML并不是按照自己书写的顺序。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Six>
    <code>1006</code>
    <desc>Some descrptions</desc>
    <name>Test6</name>
</Six>

指定XML元素的顺序

默认情况下,Jaxb编组出来的xml中的字段顺序是随机的。@XmlAccessorOrder可以控制输出顺序,它有两个值,默认为UNDEFINED(无序),XmlAccessorOrder.ALPHABETICAL是指按属性的字母顺序排序。
需要指定时可以在类名上加@XmlAccessorOrder(XmlAccessOrder.UNDEFINED)或者@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)

@Test public void test7() throws JAXBException { JAXBContext context = JAXBContext.newInstance(Seven.class); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); Seven s = new Seven(); s.setCode("1007"); s.setName("Test name"); s.setAge(22); s.setDesc("The desc"); s.setSlary(21.45); marshaller.marshal(s, System.out); }

如果要自主定义顺序,需要使用到 @XmlType,其中的 propOrder 是一个数组,可以指定顺序。

@XmlRootElement @XmlType(propOrder = {"code", "name", "age", "desc"}) public class Seven { private String code; private String name; private String desc; private int age; private double slary; // setters,getters @XmlTransient public void setSlary(double slary) { this.slary = slary; } }
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<seven>
    <code>1007</code>
    <name>Test name</name>
    <age>22</age>
    <desc>The desc</desc>
</seven>

对于没有 @XmlTransient 标注过的属性,必须出现在 @XmlType 的propOrder列表中。否则 会报错com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions 属性slary已存在, 但未在 @XmlType.propOrder 中指定

关于XmlAccessType

JAXB默认会处理 Java 的 Field 和 setters/getters 方法,有时候,我喜欢将各种注解标注在 Java 字段上,于是可能会发生如下异常。

com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions 类的两个属性具有相同名称 "code"

这是因为我在字段上标注过一个 JAXB 注解。

@XmlElement private String code;

这样 JAXB 在处理时,发现同名的属性,就会报错,像我这样喜欢将注解放置在字段上的话,需要指定一个@XmlAccessorType,将其值指定为 Field 就可以了。

@XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Eight { @XmlElement private String code; private String name; public String getCode() { return code; } // @XmlElement public void setCode(String code) { this.code = code; } public String getName() { return name; } public void setName(String name) { this.name = name; } }

当然,这时也不能同时在 setters 方法上添加注解,否则还是会出错。注释掉的代码如果放开的话,会报同样的错。因为@XmlElement是最底层的注解,会覆盖在类上标注的@XmlAccessorType(XmlAccessType.FIELD)
比如有时候,Java对象有很多属性,我只希望将其中的几个编组为XML,在其他属性或者 setters 方法上标注@XmlTransient显得有些笨拙,就可以先设置@XmlAccessorType(XmlAccessType.NONE),然后在需要编组的字段上标注@XmlElement

@Test public void test9() throws JAXBException { JAXBContext context = JAXBContext.newInstance(Nine.class); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); Nine n = new Nine(); n.setCode("1009"); n.setName("Test case"); n.setAge(22); n.setDesc("The desc"); n.setSlary(29.99); marshaller.marshal(n, System.out); }

标注@XmlAccessorType(XmlAccessType.NONE),暗示所有的属性不能编组为XML。

@XmlRootElement @XmlAccessorType(XmlAccessType.NONE) public class Nine { @XmlElement(name = "id") private String code; private String name; private String desc; private int age; private double slary; // setters,getters }

注解@XmlElement(name = "id")覆盖了类上的设置。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<nine>
    <id>1009</id>
</nine>

完整代码

可以在GitHub找到完整代码。
本节代码均在该包下:package com.example.demo.lesson10;

下节预览

下一节主要介绍更为复杂的 XML 结构,主要是多层嵌套的结构。

相关文章