Change Java's output format for nillable XML elements - java

I'm using java's jaxb to create XML files from java objects.
The problem I'm facing is the exact opposite as stated here: LinqToXml does not handle nillable elements as expected
In short: I want to properly depict members that are null in the resulting xml file.
I have following member in my class
#XmlElement (name = "order-detail", nillable = true)
private String orderDetail;
if I marshal an instance of this class, the resulting xml element is
<order-detail xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
Since also non-technicians are reading, maybe also manipulating, the file, I'd rather have it that way
<order-detail />
since I don't want to confuse them. So how can I achieve this?
UPDATE
Using an empty string instead of null
#XmlElement (name = "order-detail", nillable = true)
private String orderDetail = "";
yields
<order-detail></order-detail>
SSCCE
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
public class Example
{
public static void main(String[] args) throws JAXBException
{
Data data = new Data();
JAXBContext context = JAXBContext.newInstance(Data.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(data, System.out);
}
#XmlRootElement(name = "data")
static class Data
{
private String orderDetail;
#XmlElement (name = "order-detail", nillable = true)
public String getOrderDetail() { return orderDetail; }
public void setOrderDetail(String orderDetail) { this.orderDetail = orderDetail; }
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<data>
<order-detail xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
</data>

JAXB will marshal an empty string ("") as an empty element. You could change your code so that when the field has a value of ("") the property reports a null value.
#XmlRootElement(name = "data")
static class Data
{
private String orderDetail = "";
#XmlElement (name = "order-detail", nillable = true)
public String getOrderDetail() {
if(orderDetail.length() == 0) {
return null;
}
return orderDetail;
}
public void setOrderDetail(String orderDetail) {
if(null == orderDetail) {
this.orderDetail = "";
} else {
this.orderDetail = orderDetail;
}
}
}
Normally an XmlAdapter is used to "fix up" values, but the JAXB reference implementation does not apply an XmlAdapter to null values.
Note
In short: I want to properly depict members that are null in the
resulting xml file.
Any empty element is not a valid representation of null in XML.

Related

Read XML body element having multiple values based on different requests

I am parsing a XML request using Java. The XML structure is like this:
<?xml version="1.0" encoding="UTF-8"?>
<TestServices>
<header>
//Header Details
</header>
<body>
<ele1>
<ele2>
<ele3>534159XXXXXX0176</ele3> //Or ele_3, ele03, ele_03
</ele2>
</ele1>
</body>
</TestServices>
I have created classes for the same to read the Header and Body elements. Each node is a class and I am reading the ele3 value like this.
String ele3 = testServicesRequest.getBody().getEle1().getEle2().getEle3();
The element name for ele3 can be different based on different request. I have used Generate Java class from xsd feature in eclipse and it has generated classes like this.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"ele3"
})
public class ReqEle2 {
#XmlElement(name = "ele3", required = true)
protected String ele3;
public String getEle3() {
return ele3;
}
public void setEle3(String value) {
this.ele3 = value;
}
}
My requirement is simple. I just want to put multiple element names for single getEle3() method. eg. ele_3, ele03, ele_03 using less code changes. Or Please suggest me any other efficient way to do that.
For now I am trying to do this like this which I think is not good.
public class ReqEle3 {
#XmlElement(name = "ele03", required = true)
protected String ele3_1="";
#XmlElement(name = "ele_3", required = true)
protected String ele3_2="";
#XmlElement(name = "ele3", required = true)
protected String ele3_3="";
#XmlElement(name = "ele3_old", required = true)
protected String ele3_4="";
public String getEle3() {
if(ele3_1 != null && !ele3_1.isEmpty()){
return ele3_1;
}
else if(ele3_2 != null && !ele3_2.isEmpty()){
return ele3_2;
}
else if(ele3_3 != null && !ele3_3.isEmpty()){
return ele3_3;
}
else if(ele3_4 != null && !ele3_4.isEmpty()){
return ele3_4;
}
return "";
}
}
You can write custom deserialiser for ele3 node. To be precise, custom deserialiser for ele2 node because this is the last constant node. Below example contains only required part to understand the solution:
import org.w3c.dom.Element;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.io.StringReader;
public class JaxbApp {
public static void main(String[] args) throws Exception {
JAXBContext jaxbContext = JAXBContext.newInstance(ReqEle1.class);
String xml0 = "<ele1><ele2><ele3>534159XXXXXX0176</ele3></ele2></ele1>";
String xml1 = "<ele1><ele2><ele_3>534159XXXXXX0176</ele_3></ele2></ele1>";
String xml2 = "<ele1><ele2><ele03>534159XXXXXX0176</ele03></ele2></ele1>";
for (String xml : new String[]{xml0, xml1, xml2}) {
StringReader reader = new StringReader(xml);
Object unmarshal = jaxbContext.createUnmarshaller().unmarshal(reader);
System.out.println(unmarshal);
}
}
}
#XmlRootElement(name = "ele1")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {"ele2"})
class ReqEle1 {
#XmlJavaTypeAdapter(ReqEle2XmlAdapter.class)
#XmlElement(name = "ele2", required = true)
protected ReqEle2 ele2;
// getters, setters
}
class ReqEle2XmlAdapter extends XmlAdapter<Object, ReqEle2> {
#Override
public ReqEle2 unmarshal(Object v) {
Element element = (Element) v;
ReqEle2 reqEle2 = new ReqEle2();
reqEle2.setEle3(element.getFirstChild().getTextContent());
return reqEle2;
}
#Override
public Object marshal(ReqEle2 v) throws Exception {
return null; // Implement if needed
}
}
class ReqEle2 {
protected String ele3;
// getters, setters
}
Above code prints:
ReqEle1{ele2=ReqEle2{ele3='534159XXXXXX0176'}}
ReqEle1{ele2=ReqEle2{ele3='534159XXXXXX0176'}}
ReqEle1{ele2=ReqEle2{ele3='534159XXXXXX0176'}}
See also:
JAXB #XmlAdapter for arbitrary XML

Use JAXB to create Object from XML String with Multiple Groups in XML file

I need to convert XML string into java object.
This is the XML File
<?xml version="1.0" encoding="UTF-8"?>
<DATA_DS>
<G_1>
<TERM_ID>4</TERM_ID><NAME>30 Net</NAME>
</G_1>
</DATA_DS>
I have created Class like this;
#XmlRootElement(name = "DATA_DS")
#XmlAccessorType(XmlAccessType.FIELD)
public class PaymentTerm {
#XmlElement(name = "TERM_ID")
private double termId;
#XmlElement(name = "NAME")
private String termName;
public double getTermId() {
return termId;
}
public void setTermId(double termId) {
this.termId = termId;
}
public String getTermName() {
return termName;
}
public void setTermName(String termName) {
this.termName = termName;
}
}
In Main Class
jaxbContext = JAXBContext.newInstance(PaymentTerm.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(xmlString);
PaymentTerm paymentTerm = (PaymentTerm) unmarshaller.unmarshal(reader);
This doesn't unmarshell the XML string properly because of nested groups in XML file.
If I remove the G_1 group from XML file then it convert perfectly. I need to do conversion with G_1 group
Where I have to fix the code?
<DATA_DS> contains one element, <G_1>, which itself contains two elements, <TERM_ID> and <NAME>, so your objects needs to reflect that, i.e. the class representing <DATA_DS> must have one field, typed to be a class representing <G_1>, which must have two fields.
Where I have to fix the code?
You need to create a class for <G_1>:
#XmlRootElement(name = "DATA_DS")
#XmlAccessorType(XmlAccessType.FIELD)
public class PaymentTerm {
#XmlElement(name = "G_1", required = true)
private PaymentGroup group;
}
#XmlAccessorType(XmlAccessType.FIELD)
public class PaymentGroup {
#XmlElement(name = "TERM_ID", required = true)
private double termId;
#XmlElement(name = "NAME", required = true)
private String termName;
}
You should also consider why <G_1> exists, e.g. can there be more than one <G_1> inside <DATA_DS>? If so, make it a list:
#XmlElement(name = "G_1", required = true)
private List<PaymentGroup> groups;

Casting null java.lang.Integer from JAXB produces 0, not null

I have the following XML:
<person>
<id/>
<Identifier/>
<Surname>TEST</Surname>
<Forename1>TEST</Forename1>
<Forename2/>
<Title>MR</Title>
<Gender>M</Gender>
</person>
Produced by serialising the following class:
package anonymised.packagename;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"Id",
"Identifier",
"Surname",
"Forename1",
"Forename2",
"Title",
"Sex"
})
#XmlRootElement(name = "person")
public class person{
#XmlElement(name = "id", nillable=true)
protected Integer Id;
public Integer getId(){
return Id;
}
public void setId(Integer value){
this.Id = value;
}
#XmlElement(name = "Identifier", nillable=true)
protected String Identifier;
public String getIdentifier(){
return Identifier;
}
public void setIdentifier(String value){
this.Identifier = value;
}
#XmlElement(name = "Surname", nillable=true)
protected String Surname;
public String getSurname(){
return Surname;
}
public void setSurname(String value){
this.Surname = value;
}
#XmlElement(name = "Forename1", nillable=true)
protected String Forename1;
public String getForename1(){
return Forename1;
}
public void setForename1(String value){
this.Forename1 = value;
}
#XmlElement(name = "Forename2", nillable=true)
protected String Forename2;
public String getForename2(){
return Forename2;
}
public void setForename2(String value){
this.Forename2 = value;
}
#XmlElement(name = "Title", nillable=true)
protected String Title;
public String getTitle(){
return Title;
}
public void setTitle(String value){
this.Title = value;
}
#XmlElement(name = "Gender", nillable=true)
protected String Gender;
public String getGender(){
return Gender;
}
public void setGender(String value){
this.Gender = value;
}
}
I used the "set" methods on the object and JAXB serialisation to produce the XML above, choosing java.lang.Integer objects precisely because they are nullable.
I then used the following code to deserialise the XML (represented by xmlString) back to the object:
DocumentBuilder builder = fac.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(xmlString)));
String className = "anonymised.packagename." + doc.getDocumentElement().getNodeName();
Class<?> c = Class.forName(className);
JAXBContext context = JAXBContext.newInstance(c);
Unmarshaller um = context.createUnmarshaller();
Object o = c.cast(um.unmarshal(new StringReader(xmlString)));
JAXBContext jbc = JAXBContext.newInstance(c);
Marshaller jm = jbc.createMarshaller();
jm.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jm.marshal(o, new File("C:\\person.xml"));
Which produced the following XML file:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
<id>0</id>
<Identifier/>
<Surname>TEST</Surname>
<Forename1>TEST</Forename1>
<Forename2></Forename2>
<Title>MR</Title>
<Gender>M</Gender>
</person>
The java.lang.Integer that was null is now 0. How can this be? Judging by breakpoints, the change from null to 0 occurs during the casting process, but I can't figure out why exactly this is. Is there some kind of conversion to a simple int during the casting process? Is there a way to avoid this phenomenon? I thought adding "nillable" to the XML Element declaration would help, but it hasn't.
JAXB doesn't consider any empty element to be a valid representation of null. If you introspect the indentifier property of type String you'll see it is "" after you unmarshal the empty element.
The two valid representations of null are:
Missing element, corresponds to minOccurs="0" in the XML Schema. This is the default behaviour.
xsi:nil="true" on the element. This corresponds to nillable="true" in the XML Schema. If you annotate your property with #XmlElement(nillable=true) you will get this behaviour.
UPDATE
Thanks for your answer. On point 2, I have annotated my property with
#XmlElement(nillable = true) in the class above, but I haven't got the
behaviour you describe in my XML. Does your answer mean that, in the
production of any XML that should be null, the attribute xsi:nil needs
to explicitly be set to true?
When you have #XmlElement(nillable=true) the expectation is that the corresponding XML element has the xsi:nil attribute set to true like the following:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
<id xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
</person>
I'm happy with the idea that the Identifier element is an empty
String, not null, but the id element, because it's an Integer should
be null, not 0, right?
You can definitely make that argument. With the JAXB RI if you change your Integer property to Long you will see the behaviour you are looking for.

Getting incorrect values while converting an XML into a Java Object

I m trying to convert XML file into Java Object using Jaxb unmarshalling.
public static void main(String[] args) {
String input = "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">"+
" <key>1</key>" +
"<income>100.335</income>" +
"</project>" ;
NexusClient c1 = new NexusClient();
c1.getObject(input);
}
/*********/
public boolean getObject(String input) {
InputSource inputSource = new InputSource(new StringReader(input));
System.out.println(inputSource);
try {
JAXBContext jaxbContext = JAXBContext
.newInstance(mavenEntity.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
mavenEntity mavenObject = (mavenEntity) jaxbUnmarshaller
.unmarshal(inputSource);
System.out.println("Success"+mavenObject.getIncome());
} catch (JAXBException e) {
System.out.println("Unable to parse the XML Context");
e.printStackTrace();
return false;
}
return true;
}
I m facing an issue while trying to extract "Income" tag information. I couldn't extract correct values using Jaxb. My pojo class is :
#XmlRootElement(name = "project", namespace = "http://maven.apache.org/POM/4.0.0")
#XmlAccessorType(XmlAccessType.FIELD)
public class mavenEntity {
#XmlElement(name = "key", type = String.class)
private String key;
#XmlElement(name = "income", type = String.class)
private String income;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getIncome() {
return income;
}
public void setIncome(String income) {
this.income = income;
}
}
I m getting Null as output for any tag in XML. I guess there is some problem with my name space in XML Annotation. But I really don't understand what it is. Before posting this, I did some groundwork by referring to few links similar to this But still my result is incorrect. Can some one help me out.
The namespace qualification in your model does not match the document. Instead of specifying the namespace on #XmlRootElement and all instances of #XmlElement you can specify the namespace qualification at the package level using #XmlSchema.
package-info.java
#XmlSchema(
namespace = "http://maven.apache.org/POM/4.0.0",
elementFormDefault = XmlNsForm.QUALIFIED)
package org.example.foo;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
mavenEntity.java
I have removed the unnecessary annotations from this class (see: http://blog.bdoughan.com/2012/07/jaxb-no-annotations-required.html).
package org.example.foo;
import javax.xml.bind.annotation.XmlSchema;
#XmlRootElement(name = "project")
#XmlAccessorType(XmlAccessType.FIELD)
public class mavenEntity {
private String key;
private String income;
}
For More Information
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
You will need to add namespace to your #XmlElement annotated fields too
#XmlElement(name = "key", namespace = "http://maven.apache.org/POM/4.0.0")
private String key;
#XmlElement(name = "income", namespace = "http://maven.apache.org/POM/4.0.0")
private String income;
That's because your root element has a particular namespace. Since the nested elements don't have namespace prefix, they are using the root's. I guess this is required by JAXB.
Some alternatives and/or explanations here and here.

JAXB - empty tags with no xsi:nil

I have a String property in an object annotated as follows:
#XmlElement(name = "Item", required = true, nillable = true)
private String item;
The result after marshaling is
<Item xsi:nil="true"/>
while I would like it to be
<Item/>
since the third-party service accepting my XML messages wants it like the latter case. I am using jaxb2. Does anyone knows how I could possibly do this?
Thanks a lot
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
The following example requires the use of MOXy as the JAXB provider. This is because the JAXB RI does not call the XmlAdapter when the field/property is null. For information on specifying MOXy as your JAXB provider see:
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
StringAdapter
The XmlAdapter will convert the String value to an object with a property annotated with #XmlValue.
package forum8986842;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class StringAdapter extends XmlAdapter<StringAdapter.AdaptedString, String>{
#Override
public String unmarshal(AdaptedString adaptedString) throws Exception {
if(null == adaptedString) {
return null;
}
String string = adaptedString.value;
if("".equals(string)) {
return null;
}
return string;
}
#Override
public AdaptedString marshal(String string) throws Exception {
AdaptedString adaptedString = new AdaptedString();
adaptedString.value = string;
return adaptedString;
}
public static class AdaptedString {
#XmlValue public String value;
}
}
Root
The #XmlJavaTypeAdapter annotation is used to specify the XmlAdapter:
package forum8986842;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement(name="Root")
public class Root {
private String item;
#XmlElement(name = "Item", required = true, nillable = true)
#XmlJavaTypeAdapter(StringAdapter.class)
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
}
Demo
The following code can be used to demonstrate the above mapping. Two documents are used one with an empty Item element, and the other with a populated Item element.
package forum8986842;
import java.io.StringReader;
import javax.xml.bind.*;
public class Demo {
private JAXBContext jc;
public Demo() throws JAXBException {
jc = JAXBContext.newInstance(Root.class);
}
public static void main(String[] args) throws Exception {
Demo demo = new Demo();
demo.demo("<Root><Item/></Root>");
demo.demo("<Root><Item>Hello World</Item></Root>");
}
private void demo(String xml) throws JAXBException {
System.out.println("\n\nINPUT: " + xml);
StringReader stringReader = new StringReader(xml);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Root root = (Root) unmarshaller.unmarshal(stringReader);
System.out.println("ITEM: " + root.getItem());
System.out.print("OUTPUT: ");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
marshaller.marshal(root, System.out);
}
}
Output
The following is the output from running the demo code:
INPUT: <Root><Item/></Root>
ITEM: null
OUTPUT: <Root><Item/></Root>
INPUT: <Root><Item>Hello World</Item></Root>
ITEM: Hello World
OUTPUT: <Root><Item>Hello World</Item></Root>
For More Information
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
http://blog.bdoughan.com/2011/06/jaxb-and-complex-types-with-simple.html
http://blog.bdoughan.com/search/label/XmlAdapter
I found changing the xsd was easier
<xs:element name="name">
<xs:complexType/>
</xs:element>
and in your code, when you auto generate your java src/classes
you would specify new Name and set the Name to whichever object name belongs under

Categories