1. 程式人生 > >JAXB 教程 複雜XML生成

JAXB 教程 複雜XML生成

摘要: JAXB 作為JDK的一部分,能便捷地將Java物件與XML進行相互轉換,本教程從實際案例出發來講解JAXB 2 的那些事兒。完整版目錄

上一節的Java物件都是相對基礎的資料型別,這一節的資料多為複雜的型別。

簡單物件巢狀

Order物件中包含Product物件,這在專案中是常見情形。

	public void test1() throws JAXBException {
		Product p = new Product();
		p.setId("1100");
		p.setName("Apple");
		
		Order order = new Order();
		order.
setId("1101"); order.setPrice(23.45); order.setProduct(p); JAXBContext context = JAXBContext.newInstance(Order.class); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(order, System.out); }

Order物件的第三個屬性是個複雜的資料型別。

@XmlRootElement(name = "Order")
public class Order {

	private String id;
	private Double price;
	private Product product;
	//setters, getters
}

被巢狀的 Product 不需要使用 @XmlRootElement註解,使用註解 @XmlAccessorType是因為我喜歡將註解標註在欄位Filed上面。

@XmlAccessorType(XmlAccessType.FIELD)
public class Product {
	@XmlAttribute
private String id; private String name; //setters, getters }

生成的XML含有兩個層級。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Order>
    <id>1101</id>
    <price>23.45</price>
    <product id="1100">
        <name>Apple</name>
    </product>
</Order>

含有List型別

一般來說,一個Order不止含有一個Product,這時就需要使用到 List 了。

	@Test
	public void test2() throws JAXBException {
		Product p1 = new Product();
		p1.setId("11021");
		p1.setName("Apple");
		
		Product p2 = new Product();
		p2.setId("11022");
		p2.setName("Banana");
		
		List<Product> list = Arrays.asList(p1,p2);
		
		Order2 order = new Order2();
		order.setId("1102");
		order.setPrice(45.67); 
		order.setProduct(list);
		
		JAXBContext context = JAXBContext.newInstance(Order2.class);
		Marshaller marshaller = context.createMarshaller();
		marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
		
		marshaller.marshal(order, System.out);
	}

物件 Order 的第三個欄位是 List 型別。Product和上例中的一樣。

@XmlRootElement(name = "Order")
public class Order2 {

	private String id;
	private Double price;
	private List<Product> product;
	//setters, getters
}

生成的XML中 product 重複出現多次。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Order>
    <id>1102</id>
    <price>45.67</price>
    <product id="11021">
        <name>Apple</name>
    </product>
    <product id="11022">
        <name>Banana</name>
    </product>
</Order>

迴圈資料被包裹

上例中的Product是散落著的資料,有時候可能需要將其包裹起來,這時只需要改動 Order 物件,新增@XmlElementWrapper

@XmlRootElement(name = "Order")
@XmlAccessorType(XmlAccessType.FIELD)
public class Order3 {

	private String id;
	private Double price;
	@XmlElementWrapper(name = "Products")
	private List<Product> product;

編組過程和上例中的相同,生成的XML包含了一個 Products 包裹著所有的 Product

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Order>
    <id>1102</id>
    <price>45.67</price>
    <Products>
        <product id="11021">
            <name>Apple</name>
        </product>
        <product id="11022">
            <name>Banana</name>
        </product>
    </Products>
</Order>

關於簡單動態XML

有一點需要注意,之前所有的欄位都指定了資料,如果不指定資料,就能生成不一樣的XML。

	@Test
	public void test4() throws JAXBException {
		Product p1 = new Product();
		p1.setId("11041");
		
		Product p2 = new Product();
		p2.setId("11042");
		p2.setName("Grape");
		
		List<Product> list = Arrays.asList(p1,p2);
		
		Order3 order = new Order3();
		order.setId("1104");
		order.setProduct(list);
		
		JAXBContext context = JAXBContext.newInstance(Order3.class);
		Marshaller marshaller = context.createMarshaller();
		marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
		
		marshaller.marshal(order, System.out);
	}

生成的XML只包含上面指定資料的欄位。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Order>
    <id>1104</id>
    <Products>
        <product id="11041"/>
        <product id="11042">
            <name>Grape</name>
        </product>
    </Products>
</Order>

動態 XML Element

有時候,Products元素下面並不只是加入 product ,可能動態加入各種商品。 這次的模擬的商品是糕點與餅乾。

這裡的 Cake 只包含一個欄位,需要注意的是 @XmlRootElement不能少。

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

這裡的 Biscuit 也只包含一個欄位。

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

Order的第三個欄位是List,但是沒有指定一個特定物件,用了Object代指所有,還有一個@XmlAnyElement是重要的註解,用來標註所有的Element。

@XmlRootElement(name = "Order")
@XmlAccessorType(XmlAccessType.FIELD)
public class Order4 {

	private String id;
	private Double price;
	@XmlElementWrapper(name = "Products")
	@XmlAnyElement
	private List<Object> product;
	//setters, getters
}
	

下面用來模擬資料生成,注意的是JAXBContext需要註冊所有需要編組的Java bean。這一點和之前的例子是不同的。

	@Test
	public void test4() throws JAXBException {
		Cake cake = new Cake();
		cake.setName("Nobel");
		
		Biscuit biscuit = new Biscuit();
		biscuit.setName("PB");
		
		List<Object> list = Arrays.asList(cake,biscuit);
		
		Order4 order = new Order4();
		order.setId("1104");
		order.setProduct(list);
		
		JAXBContext context = JAXBContext.newInstance(Order4.class, Cake.class, Biscuit.class);
		Marshaller marshaller = context.createMarshaller();
		marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
		
		marshaller.marshal(order, System.out);
	}

生成的XML包含了一個 Products ,其資料結構在Order中並沒有指定,方便後期擴充套件。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Order>
    <id>1104</id>
    <Products>
        <Cake>
            <name>Nobel</name>
        </Cake>
        <Biscuit>
            <name>PB</name>
        </Biscuit>
    </Products>
</Order>

XML 動態 Attribute

與動態Element對應的是Attribute,不過需要注意的是,動態 Attribute 需要的是 Map ,而且其 key 的型別需要指定為 QName,這個QName在複雜的 XML 生成時有很大用處。一般使用到的是其QName(name),它還有一個形式為QName(name,namespace),可以指定名稱空間,在某些場景下有不可替代的作用。

@XmlRootElement(name = "Order")
@XmlAccessorType(XmlAccessType.FIELD)
public class Order5 {
	@XmlAnyAttribute
	private Map<QName, String> properties;
	private Product product;
	//setters, getters
}

Product與之前的一樣,Map的key是特殊型別。

	@Test
	public void test5() throws JAXBException {
		Product p = new Product();
		p.setId("1105");
		p.setName("Apple");
		
		Map<QName, String> map = new HashMap<>();
		map.put(new QName("id"), "1105");
		map.put(new QName("classification"), "food");
		map.put(new QName("type"), "fruit");
		
		Order5 order = new Order5();
		order.setProduct(p);
		order.setProperties(map);
		
		JAXBContext context = JAXBContext.newInstance(Order5.class);
		Marshaller marshaller = context.createMarshaller();
		marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
		
		marshaller.marshal(order, System.out);
	}

僅關注 Order 的 Attribute , 可以發現我們在 Order 中沒有指定任何與之相關的欄位,只是在HashMap中加了幾組資料,現在編組成為了Attribute。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Order id="1105" classification="food" type="fruit">
    <product id="1100">
        <name>Apple</name>
    </product>
</Order>

完整程式碼

可以在GitHub找到完整程式碼。 本節程式碼均在該包下:package com.example.demo.lesson11;

下節預覽

本節介紹了 JAXB 編組為複雜 XML 的場景,下一節還將繼續擴充套件一些實用的場景。