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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jiangchao858/article/details/82795134

摘要: 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 结构,主要是多层嵌套的结构。

猜你喜欢

转载自blog.csdn.net/jiangchao858/article/details/82795134