还是以在第一节介绍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"));
方法一:使用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)); }