2016年11月6日 星期日

JAXB

JAXB全名為Java Architecture for XML Binding
是一種java和xml轉換的工具
以下以一個一對多的方式為例


※Employee.java

public class Employee {
    private String name;
    private int id;
    
    // setter/getter...
    
    public Employee(String name, int id) {
        super();
        this.name = name;
        this.id = id;
    }
    
    public Employee() {}
}

※預設建構子在java轉xml時會用到


※Dept.java

@XmlRootElement
public class Dept {
    private String name;
    private int did;
    private List<Employee> emps;
    
    // setter/getter...
    
    public Dept(String name, int did, List<Employee> emps) {
        super();
        this.name = name;
        this.did = did;
        this.emps = emps;
    }
    
    public Dept() {}
}

※預設建構子在xml轉java時會用到


※java轉xml

public void javaToXml() throws JAXBException {
    JAXBContext jaxb = JAXBContext.newInstance(Dept.class);
    Marshaller ms = jaxb.createMarshaller();
    
    List<Employee> list = new ArrayList<>();
    list.add(new Employee("Bruce", 123));
    list.add(new Employee("Jacky", 456));
    list.add(new Employee("Mary", 789));
    
    ms.marshal(new Dept("information", 2, list), System.out);
    ms.marshal(new Dept("information", 2, list), new File("D:/jaxb.xml"));
}

※JAXBContext.newInstance裡面的類別必須要有@XmlRootElement

※屬性名為xml的標籤名,一對多的Employee叫emps,所以<emps>下會有它裡面的屬性<id>和<name>

※如果屬性是Map,那會變成
<屬性名><entry><key /><value /></entry>  <entry><key /><value /></entry></屬性名>


※xml轉java

public void xmlToJava() throws JAXBException {
    String line = System.getProperty("line.separator");
    String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" + line
            + "<dept><did>2</did><emps><id>123</id><name>Bruce</name></emps>" + line
            + "<emps><id>456</id><name>Jacky</name></emps>" + line + "<emps><id>789</id><name>Mary</name></emps>"
            + line + "<name>information</name></dept>";
    
    JAXBContext jaxb = JAXBContext.newInstance(Dept.class);
    Unmarshaller um = jaxb.createUnmarshaller();
    
    // 使用String
    Dept dept1 = (Dept) um.unmarshal(new StringReader(xml));
    System.out.println(dept1.getName());
    System.out.println(dept1.getEmps().get(0).getName());
    System.out.println(dept1.getEmps().get(1).getName());
    System.out.println("==========");
    
    // 使用File
    Dept dept2 = (Dept) um.unmarshal(new File("D:/jaxb.xml"));
    System.out.println(dept2.getName());
    System.out.println(dept2.getEmps().get(0).getName());
    System.out.println(dept2.getEmps().get(1).getName());
}

※不管tag和tag之間有沒有空隔、tab、換行,我試的結果都是可以的



※@XmlRootElement、@XmlAccessorType、@XmlElement、@XmlTransient、@XmlType、@XmlAttribute、@XmlAccessorOrder

這幾個Annotation 都是在 javax.xml.bind.annotation套件裡,裡面有很多,但這幾個比較常用

※Dept.java

@XmlType(propOrder={"did", "name"})
@XmlRootElement(name = "root")
@XmlAccessorType(XmlAccessType.FIELD)
public class Dept {
    @XmlElement(name = "first")
    private String name;
    
    @XmlElement
    private int did;
    
    @XmlTransient
    private List<Employee> emps;
    
    // ...
}

※@XmlRootElement是一定要有的,預設name名稱就是class名稱,所以tag會變成Dept,如果想改可以自行定義,像我上面的例子改成root

※@XmlTransient:不產生xml

※@XmlAccessorType 有以下4種:
FIELD:產生的xml,以成員變數為名
PROPERTY:產生的xml,以setter/getter為名,setter/getter為一組,少一個叫不會產生xml
PUBLIC_MEMBER:此為預設值,以setter/getter為名,但還得是public
NONE:整個類別不產生xml

※@XmlElement是針對子元素的,如果想改名,不能為null…等,都可在這設定
要注意@XmlAccessorType的設定會影響非類別註解寫的位置
.如果@XmlAccessorType設為FIELD,那麼註解要設在field才有效

.如果@XmlAccessorType設為PROPERTY,那麼這個註解要設在getter才有效;設在setter會報錯

.如果@XmlAccessorType是預設的PUBLIC_MEMBER,不能設定@XmlElement,會報「類別有相同名稱~」的錯

.如果@XmlAccessorType設為NONE,設定@XmlElement還是有效

※@XmlType的propOrder裡面除了已經註解為@XmlTransient以外,通通都得把元素寫出來,否則會報錯,它的功能是將產生的xml,以這裡的設定為順序(不是@XmlElement裡的name)
.其他的屬性是針對Schema用的,在這裡看不出來有什麼不同

.@XmlAccessorOrder也是排序的,但只有兩個值
UNDEFINED:預設值,以java的順序為主(不是@XmlElement裡的name)
ALPHABETICAL:由a~z的順序為主(不是@XmlElement裡的name)
.兩個註解都下不會出錯,但為了必免混亂,最好只下一個

※@XmlAttribute為設定屬性的,如將did上面的註解改成這個,那did會變成Dept的屬性,當然也有name可以改變顯示的名字
.不能與@XmlElement同時使用,否則會產生「~含有互斥的註解~」的錯



※@XmlJavaTypeAdapter

為xml和java型態轉換用的,以下用一個日期轉換格式為例
使用這個annotation要寫一個class繼承XmlAdapter
我試過在Dept.java裡寫一個類別,不管是private、protected、default,都會出錯


※Dept.java

@XmlRootElement
public class Dept {
    @XmlJavaTypeAdapter(DateAdapter.class)
    private Date date;
    
    public Dept() {}
    
    public Dept(Date date) {
        this.date = date;
    }
}


※DateAdapter.java

public class DateAdapter extends XmlAdapter<String, Date> {
    DateFormat fmt = new SimpleDateFormat("yyyyMMdd HHmmss");
    
    @Override
    public Date unmarshal(String v) throws Exception {
        return fmt.parse(v);
    }
    
    @Override
    public String marshal(Date v) throws Exception {
        return fmt.format(v);
    }
}


※測試類

JAXBContext jaxb = JAXBContext.newInstance(Dept.class);
Marshaller ms = jaxb.createMarshaller();
jaxb.createMarshaller().marshal(new Dept(new Date()), System.out);



沒有留言:

張貼留言