When I marshall a java object using JAXB Marshaller, the marshaller does not create empty elements for null files in the java object. For example, I have a following java object:
public class PersonTraining {
#XmlElement(name = "Val1", required = true)
protected BigDecimal val1;
#XmlElement(name = "Val2", required = true, nillable = true)
protected BigDecimal val2;
#XmlElement(name = "Val3", required = true, nillable = true)
#XmlSchemaType(name = "dateTime")
protected XMLGregorianCalendar val3;
}
When I take an instance of this object, and marshall into an XML, I get the following (This is beacuse I did not set the value for Val2):
<PersonTraining>
<Val1>1</Val1>
<Val3>2010-01-01T00:00:00.0-05:00</Val3>
</PersonTraining>
However, I had expected hte following result from the marshalling operation (Infact, I specifically need element as well so that the XML can be validated against the XSD)
<PersonTraining>
<Val1>1</Val1>
<Val2></Val2>
<Val3>2010-01-01T00:00:00.0-05:00</Val3>
</PersonTraining>
Please let me know what option I would need to set so that the null value in the object attributes can ALSO be marshalled, and returned as empty/null elements.
Here is the marshalling code:
StringWriter sw = new StringWriter();
JAXBContext jc = JAXBContext.newInstance("person_training");
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(ptl, sw);
By default a JAXB (JSR-222) implementation will not marshal an attribute/element for null values. This will be true for the following field in your Java model.
#XmlElement(name = "Val1", required = true)
protected BigDecimal val1;
You can override this behaviour by specifying nillable=true on the #XmlElement annotation like you have done here:
#XmlElement(name = "Val2", required = true, nillable = true)
protected BigDecimal val2;
This will cause the xsi:nil="true" attribute to be leverage:
<Val2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
For more information:
http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html
Java Model
PersonTraining
Since you are annotating the fields you should make sure you specify #XmlAccessorType(XmlAccessType.FIELD) at the class or package level (see: http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html).
import java.math.BigDecimal;
import javax.xml.bind.annotation.*;
import javax.xml.datatype.XMLGregorianCalendar;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class PersonTraining {
#XmlElement(name = "Val1", required = true)
protected BigDecimal val1;
#XmlElement(name = "Val2", required = true, nillable = true)
protected BigDecimal val2;
#XmlElement(name = "Val3", required = true, nillable = true)
#XmlSchemaType(name = "dateTime")
protected XMLGregorianCalendar val3;
}
Demo Code
Demo
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(PersonTraining.class);
PersonTraining pt = new PersonTraining();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(pt, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<personTraining>
<Val2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
<Val3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
</personTraining>
Related
I am trying to marshal a java class into xml using Jaxb, but I am getting a strange error
[com.sun.istack.SAXException2: unable to marshal type "java.lang.String" as an element because it is missing an #XmlRootElement annotation]
My application is springboot and camel.
I have the following Pojo structure, which was all generated using xjc and all the fields are annotated including rootElement :
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"header",
"message"
})
#XmlRootElement(name = "tXML")
public class TXML {
#XmlElement(name = "Header", required = true)
protected Header header;
#XmlElement(name = "Message", required = true)
protected Message message;
......... getters and setters
The Message class contains a list of orders:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"distributionOrder"
})
#XmlRootElement(name = "Message")
public class Message {
#XmlElement(name = "DistributionOrder", required = true)
protected List<DistributionOrder> distributionOrder;
and the DistributionOrder contains a list of lines and other objects, but it is generated too.
#XmlRootElement(name = "DistributionOrder")
public class DistributionOrder {
#XmlElement(name = "DistributionOrderId", required = true)
protected String distributionOrderId;
#XmlElementRef(name = "OrderType", type = JAXBElement.class, required = false)
protected JAXBElement<String> orderType;
There are a lot of fields in the DistributionOrder, so I am just showing couple.
I have a REST service that is called and passed a DistributionOrder xml, I am using Jaxb to unmarshal the request Xml into a DistributionOrder and this part is working fine.
Now I need to create a TXML class and put the generated order in it and marshal it.
This is what I am doing:
ObjectFactory obj = new ObjectFactory();
String result = null;
TXML tXML = obj.createTXML();
Message msg = obj.createMessage();
Header header = obj.createHeader();
tXML.setHeader(header );
tXML.setMessage(msg);
header.setSource("HOST");
header.setActionType("CREATE");
header.setMessageType("DO-Rating");
header.setCompanyID(companyId);
header.setMsgTimeZone("EST");
msg.getDistributionOrder().add(order); //this is the order that was unmarshaled from the service
//convert it to xml
try
{
JAXBContext context = JAXBContext.newInstance(TXML.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(tXML,System.out);
}
catch (JAXBException e)
{
logger.error("MessagingBean error when trying to marshall DistributionOrder: " + order.getDistributionOrderId() + " (" + e.getMessage()!=null?e.getMessage():e.getCause()!=null?e.getCause().toString():e.getClass().getName() + ") " );
e.printStackTrace();
throw e;
}
I tried to debug and follow the error and it seems that the marshal logic goes through TXML, header, message and once it gets into DistributionOrder it throws this error:
[com.sun.istack.SAXException2: unable to marshal type "java.lang.String" as an element because it is missing an #XmlRootElement annotation]
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:301)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:226)
at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:80)
at com.shaw.csds.rating.service.MessagingBean.createMessage(MessagingBean.java:58)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
If I continue debugging, it looks like it continues marshaling all the fields in the DistributionOrder and all the lines, etc... but at the end it throws the error above and get into the catch section.
I am not sure what is wrong since everything is annotated correctly.
Thank you in advance.
all, I have a package full of Beans generated from xsd with Jaxb.
I want to use some of them into another Bean to marshal/unmarshal an XML like this:
<WrongDocument>
<test>test label</test>
<CBISDDReqLogMsg xmlns="urn:CBI:xsd:CBISDDReqLogMsg.00.01.00">
<GrpHdr>
....
</GrpHdr>
<PmtInf>
</PmtInf>
</CBISDDReqLogMsg>
</WrongDocument>
the root Bean is
#XmlRootElement(name="WrongDocument")
#XmlType(name = "", propOrder = {
"test",
"CBISDDReqLogMsg"
})
#XmlAccessorType(XmlAccessType.FIELD)
public class WrongDocumentDTO implements Serializable {
private static final long serialVersionUID = 8545918230166653233L;
#XmlElement(required = true, type = String.class, nillable = true)
protected String test;
#XmlElement(required = true)
protected CBISDDReqLogMsg000100 CBISDDReqLogMsg;
....
}
and the CBISDDReqLogMsg000100 is
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "CBISDDReqLogMsg.00.01.00", propOrder = {
"grpHdr",
"pmtInf"
})
public class CBISDDReqLogMsg000100
implements Serializable
{
private final static long serialVersionUID = 1L;
#XmlElement(name = "GrpHdr", required = true)
protected CBIGroupHeader2 grpHdr;
#XmlElement(name = "PmtInf", required = true)
protected List<PaymentInstructionInformation2> pmtInf;
....
}
For CBISDDReqLogMsg000100 namespace is defined with package-info file.
This is the code for unmarshalling:
jc = JAXBContext.newInstance(WrongDocumentDTO.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
WrongDocumentDTO wrongDocumentDTO = unmarshaller.unmarshal(source, WrongDocumentDTO.class).getValue();
Unfortunately, inside my wrongDocumentDTO i have test field populated with the right value, however CBISDDReqLogMsg is null.
How can i resolve this issue?
thank you in advance
You will need to specify the namespace specifically on the element if it is not default for the whole package.
#XmlElement(required = true, namespace = "urn:CBI:xsd:CBISDDReqLogMsg.00.01.00")
protected CBISDDReqLogMsg000100 CBISDDReqLogMsg;
I am looking to control where JAXB generates the xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" declaration when marshalling to XML. I've seen solutions like this to add it to the root element using the JAXB_SCHEMA_LOCATION property, however I don't want it on the root node, I want it somewhere in between. Here's what I've got:
#XmlRootElement(name = "RootNode")
#XmlAccessorType(XmlAccessType.NONE)
public class RootNode {
#XmlElement(name = "IntermediateNode")
private IntermediateNode intermediateNode;
//getter & setter
}
#XmlAccessorType(XmlAccessType.NONE)
public class IntermediateNode {
#XmlElement(name = "MyEntity")
private MyEntity myEntity;
//getter and setter
}
#XmlAccessorType(XmlAccessType.NONE)
public class MyEntity {
#XmlElement(name = "Name")
private String name;
#XmlElement(name = "Title", nillable = true)
private String title;
//getters and setters
}
Serialize like:
MyEntity myEntity = new MyEntity();
myEntity.setName("George");
myEntity.setTitle(null);
IntermediateNode intNode = new IntermediateNode();
intNode.setMyEntity(myEntity);
RootNode rootNode = new RootNode();
rootNode.setIntermediateNode(intNode);
JAXBContext context = JAXBContext.newInstance(RootNode.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(rootNode, System.out);
Produces XML like:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RootNode>
<IntermediateNode>
<MyEntity>
<Name>George</Name>
<Title xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
</MyEntity>
</IntermediateNode>
</RootNode>
But what I want is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RootNode>
<IntermediateNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<MyEntity>
<Name>George</Name>
<Title xsi:nil="true"/>
</MyEntity>
</IntermediateNode>
</RootNode>
I even tried moving my IntermediateNode and MyEntity classes into their own package with a package-info.java like this, but that just rolled the xmlns:xsi up to the root element.
#javax.xml.bind.annotation.XmlSchema(
xmlns = {
#javax.xml.bind.annotation.XmlNs(prefix = "xsi", namespaceURI = "http://www.w3.org/2001/XMLSchema-instance")
},
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.example.intermediate;
Is it possible to get what I want?
i am trying to use one class to map the response i get from an XML request.
But the xml response differs, depending own some settings. For example in a response i get the tag "owner" which is filled with the ID of the owner object. If i add a setting in my request i will get back the full owner data, like the firstname and lastname. Now i want to map the owner tag to either a String variable or a Class depending on the response.
Example :
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "domain")
public class Response {
#XmlElement
private String name;
#XmlElement(name = "owner")
private String ownerSimple;
#XmlElement(name = "owner")
private Owner ownerComplex;
}
#XmlRootElement(name = "ownerc")
public class OwnerC {
#XmlElement
int id;
#XmlElement
String fname;
#XmlElement
String lname;
}
XML to map :
<response>
<name>Foo</name>
<owner>1234</owner> <!-- in this case it's only a id -->
</response>
<response>
<name>Foo</name>
<owner> <!-- in this case it's the owner class -->
<id>1234</id>
<fname>Jon</fname>
<lname>Doe</lname>
</owner>
</response>
You can use #XmlAnyElement(lax=true) to handle this use case. This annotation allows you to unmarshall any XML to a Java object (DOM Node). In a second step, it is possible to unmarshall the Node to the required Object
Response
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "domain")
public class Response {
#XmlElement
private String name;
#XmlAnyElement(lax=true)
private Object owner;
private String ownerSimple;
#XmlTransient
private Owner ownerComplex;
Owner
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "owner")
public class Owner {
#XmlElement
int id;
#XmlElement
String fname;
#XmlElement
String lname;
Unmarshaller
//Unmarshaller. Step 1 - Decodes Response and set a DOM Node at Owner
//Important. Owner class must not be present in JAXB context, letting next step to decode the object properly.
//Owner variable at Response class is annotated with #XmlTransient
JAXBContext jaxbContext = JAXBContext.newInstance(Response.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Response response = (Response) jaxbUnmarshaller.unmarshal(reader);
//Unmarshaller. Step 2. Convert Node to the suitable Object
//Considering both cases, simple-> String complex -> Owner Object.
String ownerSimple = ((Node)response.getOwner()).getFirstChild().getNodeValue();
if (ownerSimple != null){
response.setOwnerSimple(ownerSimple);
} else {
JAXBContext jaxbContextOwner = JAXBContext.newInstance(Owner.class);
Unmarshaller jaxbUnmarshallerOwner = jaxbContextOwner.createUnmarshaller();
Owner ownerComplex = (Owner) jaxbUnmarshallerOwner.unmarshal((Node)response.getOwner());
response.setOwnerComplex(ownerComplex);
}
//Marshaller to system.out. Your object is well mapped in both cases
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(rx, System.out);
We are unmarshalling a response from http://xmlgw.companieshouse.gov.uk/. This is the text sent to the marshall:
<NameSearch xmlns="http://xmlgw.companieshouse.gov.uk/v1-0/schema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlgw.companieshouse.gov.uk/v1-0/schema http://xmlgw.companieshouse.gov.uk/v1-0/schema/NameSearch.xsd">
<ContinuationKey>...</ContinuationKey>
<RegressionKey>...</RegressionKey>
<SearchRows>20</SearchRows>
<CoSearchItem>
<CompanyName>COMPANY NAME</CompanyName>
<CompanyNumber>23546457</CompanyNumber>
<DataSet>LIVE</DataSet>
<CompanyIndexStatus>DISSOLVED</CompanyIndexStatus>
<CompanyDate></CompanyDate>
</CoSearchItem>
// more CoSearchItem elements
</NameSearch>
The model of CoSearchItem is like this:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "CoSearchItem", propOrder = {
"companyName",
"companyNumber",
"dataSet",
"companyIndexStatus",
"companyDate",
"searchMatch"
})
public class CoSearchItem {
#XmlElement(name = "CompanyName", required = true)
protected String companyName;
#XmlElement(name = "CompanyNumber", required = true)
protected String companyNumber;
#XmlElement(name = "DataSet", required = true)
protected String dataSet;
#XmlElement(name = "CompanyIndexStatus")
protected String companyIndexStatus;
#XmlElement(name = "CompanyDate")
#XmlSchemaType(name = "date")
protected XMLGregorianCalendar companyDate;
#XmlElement(name = "SearchMatch")
protected String searchMatch;
// getters and setters
}
NameSearch model has this structure:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "NameSearch", namespace = "http://xmlgw.companieshouse.gov.uk/v1-0/schema", propOrder = {
"continuationKey",
"regressionKey",
"searchRows",
"coSearchItem"
})
#XmlRootElement(name = "NameSearch", namespace = "http://xmlgw.companieshouse.gov.uk/v1-0/schema")
public class NameSearch {
#XmlElement(name = "ContinuationKey", required = true)
protected String continuationKey;
#XmlElement(name = "RegressionKey", required = true)
protected String regressionKey;
#XmlElement(name = "SearchRows", required = true)
protected BigInteger searchRows;
#XmlElement(name = "CoSearchItem")
protected List<CoSearchItem> coSearchItem;
// setters and getters
}
The package has this annotations:
#XmlSchema(namespace = "http://xmlgw.companieshouse.gov.uk/v1-0", elementFormDefault = XmlNsForm.QUALIFIED, //
xmlns = {
#XmlNs(prefix = "xsi", namespaceURI = "http://www.w3.org/2001/XMLSchema-instance")
}
)
package uk.gov.companieshouse;
The unmarshaling is done from the first Node extracted from a larger Document, inside an any list of items. When we parse the xml however all the fields in CoSearchItem are set to null and can't figure out the reason.
You need to use a package level #XmlSchema annotation to specify the namespace qualification for your model.
#XmlSchema(
namespace = "http://xmlgw.companieshouse.gov.uk/v1-0/schema",
elementFormDefault = XmlNsForm.QUALIFIED)
package example;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
This this specified you do not require to specify the namespace URI on the #XmlRootElement and #XmlType on your NameSearch class.
For More Information
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
The unmarshaling is done from the first Node extracted from a larger
Document, inside an any list of items.
Make sure the DOM parer used to create the nodes is namespace aware.
documentBuilderFactory.setNamespaceAware(true);
I figured out the correct answer thanks to #Blaise Doughan. After looking at the package namespace qualification I found that it was pointing to:
"http://xmlgw.companieshouse.gov.uk/v1-0"
and it should have been pointing to:
"http://xmlgw.companieshouse.gov.uk/v1-0/schema"
Not sure how that got misplaced.
I solved this by making elementFormDefault="unqualified" in the xsd before generating stubs, else make the change manually in package-info.java