Suppose we have a class Test like:
public class Test {
private final String name;
private final List<String> list = new ArrayList<>();
public Test(String name) {
this.name = name;
}
void add(String s) {
list.add(s);
}
void print() {
System.out.println("name: " + name);
for (String s : list) {
System.out.println(" - " + s);
}
}
}
Without XSteam the invariant
this.list != null
holds every time.
But if we look in the 4th test in
public static void main(String[] args) {
final XStream xstream = new XStream();
xstream.alias("test", Test.class);
// Serialize
final Test test1 = new Test("XYZ");
test1.add("One");
test1.add("Two");
//@formatter:off
/* name: XYZ
* - One
* - Two
*/
//@formatter:on
test1.print();
//@formatter:off
/* <test>
* <name>XYZ</name>
* <list>
* <string>One</string>
* <string>Two</string>
* </list>
* </test>
*/
//@formatter:on
System.out.println(xstream.toXML(test1));
// Deserialize with one list entry
final String xmlTest2 = "<test><name>XYZ</name><list><string>One</string></list></test>";
final Test test2 = (Test) xstream.fromXML(xmlTest2);
//@formatter:off
/* <test>
* <name>XYZ</name>
* <list>
* <string>One</string>
* </list>
* </test>
*/
//@formatter:on
test2.print();
// Deserialize with empty list
final String xmlTest3 = "<test><name>XYZ</name><list /></test>";
final Test test3 = (Test) xstream.fromXML(xmlTest3);
//@formatter:off
/* name: XYZ
*/
//@formatter:on
test3.print();
// Deserialize without list-tag
final String xmlTest4 = "<test><name>XYZ</name></test>";
final Test test4 = (Test) xstream.fromXML(xmlTest4);
//@formatter:off
/* name: XYZ
* Exception in thead ... NullPointerException
*/
//@formatter:on
test4.print();
}
we see a NullPointerException, because list
was not initialized.
I'd like to have the list
-element in the XML optional similiar to test4. What can I do? Because there are many classes in my datamodel similiar to Test
, I don't want to write a Converter
for every class. But suppose I would write a Converter
, how can I set the final attribute name
?
XStream uses munged constructors (http://stackoverflow.com/questions/1426106/why-are-constructors-returned-by-reflectionfactor-newconstructorforserialization) in enhanced mode (default). You can change this behavior by initializing XStream in pure mode:
XStream xstream = new XStream(new PureJavaReflectionProvider());
Another option is to access variables using getters and implement lazy initialization.
public class Test {
private final String name;
private List<String> list;
public Test(String name) {
this.name = name;
}
void add(String s) {
list.add(s);
}
List<String> getList() {
if (list == null) {
list = new ArrayList<>();
}
return list;
}
void print() {
System.out.println("name: " + name);
for (String s : getList()) {
System.out.println(" - " + s);
}
}
}