user1342086 user1342086 - 14 days ago 6
Java Question

Java - JAXB XML unmarshal with optional fields

I have issues with accessing fields of unmarshalled XML files, which contain optional tags. Here is a simple example I made up for a more complex case:

<people>
<persons>
<person>
<id>222</id>
<pets>
<pet>
<name age="2">Harry</name>
</pet>
<pet>
<name>Tiffany</name>
</pet>
</pets>
</person>
<person>
<id>111</id>
<pets>
<pet value="1"></pet>
</pets>
<spouse>Frank</spouse>
</person>
</persons>
</people>


Notice that the second person has a spouse and the first does not. Additionally, the pets of the first person have names and the pets of the second person do not. The pet named Harry has also an age attribute. What I'm trying to show is that my XML files can have varying data, because of optional fields.

Here are my model classes for JAXB:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class People {

@XmlElementWrapper
@XmlElement(name="person")
private List<Person> persons;

public List<Person> getPersons() {
return persons;
}

public void setPersons(List<Person> persons) {
this.persons= persons;
}

}

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Person {

@XmlElement
private int id;

@XmlElementWrapper
@XmlElement(name="pet")
private List<Pet> pets;

@XmlElement
private String spouse;

//getters and setters

}

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class pet {

@XmlAttribute
private int age;

@XmlValue
private String name;

//getters and setters

}


Now, let's say that I just want to print all names of pets.

for (Person person : people.getPersons()) {
for (Pet pet : person.getPets()) {
System.out.println("Pet name: " + pet.getName());
}
}


I'm getting a
NullPointerException
, if an inner tag is missing. Interestingly enough though, if you just search for a field in the people layer, it will just skip the XML tag as if it does not exist, like:

for (Person person : people.getPersons()) {
System.out.println("Spouse: " + person.getSpouse());
}


The above command works even though the first person does not have a spouse. It just provides the string "null", which works well for me.

A solution that I've tried - wrap each field in an
if
or
try-catch
statement (I prefer not to do this as there are hundreds of XML tags). Let me know if you have any suggestions. Thanks.

Answer

The NullPointerException in your example is quite obvious.

System.out.println("Pet name: " + people.getPerson().get(i).getPet().getName());

The problem is, when you have no pets,getPet() returns null. Subsequently, you call getName() on null, hence you get the exception. In your second example, the execption does not occur, because getSpouse() returns null, but System.out.println(...) converts it into the string null automatically.

Even though it may seem tedious, you will have to check your attributes explicitly for null, as you traverse your lists or fields. Never do that with try-catch! Exception handling is a heavy mechanism and you should never abuse this concept for null checks.