还是以在第一节介绍JAXB的schema为例:
- <?xml version="1.0" encoding="UTF-8"?>
- <schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.liulutu.com/students/"
- targetNamespace="http://www.liulutu.com/students/">
- <element name="students">
- <complexType>
- <sequence>
- <element name="student" type="tns:StudentType" maxOccurs="unbounded" />
- </sequence>
- </complexType>
- </element>
- <simpleType name="SexType">
- <restriction base="string">
- <enumeration value="Male"></enumeration>
- <enumeration value="Female"></enumeration>
- </restriction>
- </simpleType>
- <complexType name="StudentType">
- <attribute name="sex" type="tns:SexType"></attribute>
- <attribute name="name" type="string"></attribute>
- </complexType>
- </schema>
生成的类文件如下:
从Schema定义里可以看出,Students里包含有多个StudentType对象:
- @XmlAccessorType(XmlAccessType.FIELD)
- @XmlType(name = "", propOrder = {
- "student"
- })
- @XmlRootElement(name = "students")
- public class Students {
- @XmlElement(required = true)
- protected List<StudentType> student;
- /**
- * Gets the value of the student property.
- *
- * <p>
- * This accessor method returns a reference to the live list,
- * not a snapshot. Therefore any modification you make to the
- * returned list will be present inside the JAXB object.
- * This is why there is not a <CODE>set</CODE> method for the student property.
- *
- * <p>
- * For example, to add a new item, do as follows:
- * <pre>
- * getStudent().add(newItem);
- * </pre>
- *
- *
- * <p>
- * Objects of the following type(s) are allowed in the list
- * {@link StudentType }
- *
- *
- */
- public List<StudentType> getStudent() {
- if (student == null) {
- student = new ArrayList<StudentType>();
- }
- return this.student;
- }
- }
现在的问题是,从StudentType对象里,怎么能访问到它的父对象也就是上面的students对象呢?StudentType类如下:
- @XmlAccessorType(XmlAccessType.FIELD)
- @XmlType(name = "StudentType")
- public class StudentType {
- @XmlAttribute(name = "sex")
- protected SexType sex;
- @XmlAttribute(name = "name")
- protected String name;
- @XmlAttribute(name = "birthday")
- @XmlJavaTypeAdapter(Adapter1 .class)
- @XmlSchemaType(name = "date")
- protected Calendar birthday;
- /**
- * Gets the value of the sex property.
- *
- * @return
- * possible object is
- * {@link SexType }
- *
- */
- public SexType getSex() {
- return sex;
- }
- /**
- * Sets the value of the sex property.
- *
- * @param value
- * allowed object is
- * {@link SexType }
- *
- */
- public void setSex(SexType value) {
- this.sex = value;
- }
- /**
- * Gets the value of the name property.
- *
- * @return
- * possible object is
- * {@link String }
- *
- */
- public String getName() {
- return name;
- }
- /**
- * Sets the value of the name property.
- *
- * @param value
- * allowed object is
- * {@link String }
- *
- */
- public void setName(String value) {
- this.name = value;
- }
- /**
- * Gets the value of the birthday property.
- *
- * @return
- * possible object is
- * {@link String }
- *
- */
- public Calendar getBirthday() {
- return birthday;
- }
- /**
- * Sets the value of the birthday property.
- *
- * @param value
- * allowed object is
- * {@link String }
- *
- */
- public void setBirthday(Calendar value) {
- this.birthday = value;
- }
- }
从类定义可以看出,其中没有任何描述Students对象的信息。
解决方法:
首先,假设要解析的文件内容如下:
- <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
- <ns2:students xmlns:ns2="http://www.liulutu.com/students/">
- <student sex="Female" name="Bob" birthday="2003-11-27+08:00" />
- <student sex="Male" name="Lisa" birthday="2003-10-26+08:00" />
- </ns2:students>
(如果手头没有文件,也可以用以上模型快速的生成一个,例如:)
- StudentType studentType = new StudentType();
- studentType.setBirthday(Calendar.getInstance());
- studentType.setName("Bob");
- studentType.setSex(SexType.FEMALE);
- Students students = new Students();
- students.getStudent().add(studentType);
- JAXBContext context = JAXBContext.newInstance(Students.class);
- Marshaller marshaller = context.createMarshaller();
- marshaller.marshal(students, new File("a.xml"));
方法一:使用public void afterUnmarshal(Unmarshaller u, Object parent) 方法
首先修改一下StudentType类,增加一个用来存取父对象的变量:
- private Students parent;
- public Students getParent() {
- return parent;
- }
因为模型是由JAXB直接读取和生成的,因此我们不能通过简单的setParent()来设置parent对象。
可以简单在生成的模型类中添加一个public void afterUnmarshal(Unmarshaller u, Object parent) 方法,这个方法在每次构建完模型后都会调用(甚至我们什么接口实现,类继承都不需要声明就会自动调用),其实的parent参数就是我们期望的父模型对象,因此可以直接把这个parent对象设置成我们期望的parent对象:
- public void afterUnmarshal(Unmarshaller u, Object parent) {
- if (parent instanceof Students) {
- this.parent = (Students)parent;
- }
- }
可以检验一下:
- JAXBContext context = JAXBContext.newInstance(Students.class);
- Unmarshaller unmarshaller = context.createUnmarshaller();
- Students students = (Students) unmarshaller.unmarshal(new File("a.xml"));
- List<StudentType> student = students.getStudent();
- for(StudentType st: student){
- System.out.println(st.getName());
- System.out.println(st.getParent());
- }
方法二:使用Binder
方法一简单易用,但是缺点就是需要修改模型,这里介绍的方法就不需要修改模型,只是在读取的时候需要有点不一样:
首先我们用普遍的Dom方式读取文件内容:
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- factory.setNamespaceAware(true);
- Document document = factory.newDocumentBuilder().parse(new File("a.xml"));
然后创建JAXBContext对象和Binder对象:
- JAXBContext context = JAXBContext.newInstance(Students.class);
- Binder<Node> binder = context.createBinder();
最后用Binder对象去解析模型的和链接父子关系:
- Students students = (Students) binder.updateJAXB(document.getDocumentElement());
- List<StudentType> student = students.getStudent();
- for(StudentType st: student){
- System.out.println(st.getName());
- Node xmlNode = binder.getXMLNode(st);
- Object parent = binder.getJAXBNode(xmlNode.getParentNode());
- System.out.println(parent);
- }
方法三 使用Unmarshall.Listener监听
除了以上两种方法,还可以使用监听的方式,在marshall的过程中构建父子关系,有点类似于方法一
- JAXBContext context = JAXBContext.newInstance(Students.class);
- Unmarshaller unmarshaller = context.createUnmarshaller();
- final Map<StudentType, Students> map = new HashMap<StudentType, Students>();
- unmarshaller.setListener(new Listener() {
- public void afterUnmarshal(Object target, Object parent) {
- super.afterUnmarshal(target, parent);
- if(target instanceof StudentType && parent instanceof Students){
- map.put((StudentType)target, (Students) parent);
- }
- }
- });
- Students students = (Students) unmarshaller.unmarshal(new File("a.xml"));
- List<StudentType> student = students.getStudent();
- for(StudentType st: student){
- System.out.println(st.getName());
- System.out.println(map.get(st));
- }