Can XStream be configured to have bi-drectional (parent/child) references?
I'm using XStream to load a file structured like this:
<parent>
<child/>
<child/>
</parent>
Into a class like this:
public class Parent(){
private List<Child> children;
}
public class Child {
private Parent parent;
}
I would like to be able to do this: pa开发者_Go百科rent.getChildren().get(0).getParent()
I would like to be able to use the XML as it sits. I know I can add references to the parent under children, but that seems very redundant. I know the childs parent because of its XML structure.
Does XStream support this?
Yes Xstream supports back-links to avoid circular references. It does this by default, but its arguable whether a message that required this is really a suitable one. Its fine for just chucking some data over the network, but isn't really a "message", more a serialized object.
Your child objects will need to have a "pointer" to the parent.
One option is to use readResolve
in the parent to set the backreferences. This will be called in the same way as standard library serialization according to the XStream FAQ here: http://x-stream.github.io/faq.html#Serialization_initialize_transient
When readResolve
is called, the Parent
and all its children will have been deserialized already, so you can set the backreference at that time.
public class Parent {
private List<Child> children = new ArrayList<Child>();
private Object readResolve() {
for( Child child: children ) {
child.setParent(this);
}
return this;
}
}
If you are willing to consider using something other than XStream, then EclipseLink JAXB (MOXy) handles bi-directional properties easily with @XmlInverseReference (I'm the MOXy tech lead).
Your object model would be mapped as:
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Parent {
private List<Child> children;
@XmlElement(name="child")
public List<Child> getChildren() {
return children;
}
public void setChildren(List<Child> children) {
this.children = children;
}
}
and (note the use of @XmlInverseReference on the parent property):
import org.eclipse.persistence.oxm.annotations.XmlInverseReference;
public class Child {
private Parent parent;
@XmlInverseReference(mappedBy="children")
public Parent getParent() {
return parent;
}
public void setParent(Parent parent) {
this.parent = parent;
}
}
The demo code would look like (input.xml refers to the XML from your question):
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Parent.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Parent parent = (Parent) unmarshaller.unmarshal(new File("input.xml"));
for(Child child : parent.getChildren()) {
System.out.println(child.getParent());
}
}
}
To specify MOXy as the JAXB implementation you need to include a file called jaxb.properties in the same package as your model classes with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Below is a link to my comparison of JAXB and XStream:
- http://bdoughan.blogspot.com/2010/10/how-does-jaxb-compare-to-xstream.html
精彩评论