JAXB returns null when Xml contains no tag
I am having a problem with JAXB and Unmarshalling the following XML
<ns2:ID entry-method="manual"> 123456789012345678
<ns2:ID2>123456789012345678</ns2:ID2>
</ns2:ID>
I got the schema and using the xjc JAXB tool, it generated the following property definition:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"ID1",
"ID2",
"ID3"
})
@XmlRootElement(name = "ID")
public class ID {
@XmlElement(name = "ID1")
protected String id1;
@XmlElement(name = "ID2")
protected String id2;
@XmlElement(name = "ID3")
protected String id3;
@XmlAttribute(name = "entryMethod")
protected String entryMethod;
public String getId1() {
return id1
}
public void setId1(String value) {
this.id1 = value;
}
public String getId2() {
return id2
}
public void setId2(String value) {
this.id2 = value;
}
public String getId3() {
return id3;
}
public void setId3(String value) {
this.id3 = value;
}
public String getEntryMethod() {
if (entryMethod == null) {
return "swipe";
} else {
return entryMethod;
}
}
public void setEntryMethod(String value) {
this.entryMethod = value;
}
}
As you can see, the device that is sending the XML does not include the ID1 tag, it just adds the ID1 data as the root tag value. When Unmarshalling this Xml, any calls to getID1 will return null. I am confused about what annotations to use to modify the class to support the data in the root tag to be assigned in the id1 field.
Any ideas on what changes to the annotation would make this work?
- Tim
source to share
This is the correct XML schema content (excluding namespace) for your XML:
<xs:element name="ID" type="IdType"/>
<xs:complexType name="IdType" mixed="true">
<xs:sequence>
<xs:element name="id2" type="xs:string"/>
<xs:element name="id3" type="xs:string"/>
</xs:sequence>
<xs:attribute name="entry-method" type="xs:string"/>
</xs:complexType>
The sad consequence is that the generated IdType class contains
public List<Serializable> getContent() {
if (content == null) {
content = new ArrayList<Serializable>();
}
return this.content;
}
to contain the ID text child (children!) and all child ID elements. So the handling of the identifier could be something like this:
JAXBElement<IdType> jbe =
(JAXBElement<IdType>)u.unmarshal( new File( "mixed.xml" ) );
for( Object obj: jbe.getValue().getContent() ){
System.out.println( obj.getClass() + " " + obj );
if( obj instanceof String ){
// text child (even the blank space
} else if( obj instanceof JAXBElement ){
// process an element child wrapped into JAXBElement
}
}
source to share
Your ID class should be ...
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"content"
})
@XmlRootElement(name = "ID")
public class ID {
@XmlElementRefs({
@XmlElementRef(name = "ID3", type = JAXBElement.class),
@XmlElementRef(name = "ID2", type = JAXBElement.class),
@XmlElementRef(name = "ID1", type = JAXBElement.class)
})
@XmlMixed
protected List<Serializable> content;
@XmlAttribute(name = "entry-method")
protected String entryMethod;
/**
* Gets the value of the content 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 content property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getContent().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link String }
* {@link JAXBElement }{@code <}{@link String }{@code >}
* {@link JAXBElement }{@code <}{@link String }{@code >}
* {@link JAXBElement }{@code <}{@link String }{@code >}
*
*
*/
public List<Serializable> getContent() {
if (content == null) {
content = new ArrayList<Serializable>();
}
return this.content;
}
/**
* Gets the value of the entryMethod property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getEntryMethod() {
return entryMethod;
}
/**
* Sets the value of the entryMethod property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setEntryMethod(String value) {
this.entryMethod = value;
}
}
I tried this bean with this main method ...
public static void main(String[] args) throws JAXBException {
final JAXBContext context = JAXBContext.newInstance(ID.class);
final Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
final ID id = new ID();
id.setEntryMethod("method");
ObjectFactory o = new ObjectFactory();
id.getContent().add("sample text");
id.getContent().add(o.createIDID1("id1"));
m.marshal(id, System.out);
}
The output is generated ..
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ID entry-method="method">sample text
<ID1>id1</ID1>
</ID>
Hope I gave you all the answers to your question.
source to share
Why are you getting zero
The XML you are unmounting does not match the XML schema you generated the object model from.
<ns2:ID entry-method="manual"> 123456789012345678
<ns2:ID2>123456789012345678</ns2:ID2>
</ns2:ID>
Problem
When an element has both text and an element, it has mixed content. Your XML schema does not currently account for this.
Decision
Modify the definition of the complex type corresponding to the element ID
to have mixed="true"
. Then rebuild the JAXB model.
Why won't you like the solution
Since the type has mixed content scattered among the children, you end up with a very different JAXB model. Essentially, the class for ID
will have one property List
that contains all the content. This is required for XML trip searches.
source to share