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

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.

Related

XML parser (Unmarshall) using JaxB to get elemet from xml file

I new to jaxB XML parser. i need to get the all the attribute (idref, type, name, scope) from element tag ** I tried something but getting error.
XML File
<?xml version="1.0" encoding="windows-1252"?>
<xmi:XMIxmi:version="2.1"xmlns:uml="http://www.omg.org/spec/UML/20090901"xmlns:xmi="http://schema.omg.org/spec/XMI/2.1" xmlns:thecustomprofile="http://www.sparxsystems.com/profiles/thecustomprofile/1.0" xmlns:SysML="http://www.omg.org/spec/SysML/20161101/SysML">
<xmi:Documentation exporter="Enterprise Architect" exporterVersion="6.5" exporterID="1555"/>
<xmi:Extension extender="Enterprise Architect" extenderID="6.5">
<elements>
<element xmi:idref="EAPK_5560E5AF_736A_4703_AC79_CA3FAA60984B" xmi:type="uml:Package" name="PackageView" scope="public"></element>
<element xmi:idref="EAPK_59058493_9220_4b05_888A_67C6854C97EC" xmi:type="uml:Package" name="Read from Communication Interface" scope="public">
</element>
<element xmi:idref="EAID_870B8E54_0FF2_4a90_A9C1_23F477DF695F" xmi:type="uml:Activity" name="Read from communication interface" scope="public">
</element>
</elements>
</xmi:Extension>
</xmi:XMI>
Main Class
public class XmlElement {
public static void main(String[] args) {
JAXBContext jaxbContext;
String fileLocation = "C:\\vinoth\\XML_JaxbParser\\elements.xml";
try (FileInputStream fileInputStream = new FileInputStream(fileLocation)) {
System.out.println("******** PARSING START ********");
jaxbContext = JAXBContext.newInstance(Xmi.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
elements elements = (elements) unmarshaller.unmarshal(fileInputStream);
System.out.println(elements);
System.out.println("******** PARSING DONE ********");
} catch (Exception e) {
System.out.println("******** ERROR: SOMETHING WENT WRONG ********");
e.printStackTrace();
}
}
}
elements.java
#XmlRootElement(name = "xmi")
#XmlAccessorType(XmlAccessType.FIELD)
public class Elements {
#XmlElement(name = "elements")
private List<Elements> elements;
// Generate Getters and Setters...
#Override
public String toString() {
return "Elements [elements="+ elements +"]";
}
}
element.java
#XmlAccessorType(XmlAccessType.FIELD)
public class Element {
#XmlAttribute(name = "idref")
private String idref;
#XmlAttribute(name = "type")
private String type;
#XmlAttribute(name = "name")
private String name;
#XmlAttribute(name = "scope")
private String scope;
//Generate Getters and Setters...
#Override
public String toString() {
return "Element [idref=" + idref + ",type=" + type + ", name=" + name + ", scope=" + scope+ "]";
}
}
I need to get the the element attribute from the given XML file. I don't know where I made a mistake. Root element and parent and children, i can understand mistake from mapping section. but I couldn't sort out.
Your XML file uses many different XML namespaces,
which you need to consider in your Java code.
Especially notice, that the namespace URIs (e.g.
"http://schema.omg.org/spec/XMI/2.1") are the
only relevant thing. The namespace prefixes (e.g. xmi:) are not
relevant for Java. They were only invented to make the XML
easier to read for humans.
I recommend to define all your XML namespace URIs as Java
string contants to avoid typing them many times in your code:
public class NameSpaces {
public final static String UML = "http://www.omg.org/spec/UML/20090901";
public final static String XMI = "http://schema.omg.org/spec/XMI/2.1";
public final static String THE_CUSTOM_PROFILE = "http://www.sparxsystems.com/profiles/thecustomprofile/1.0";
public final static String SYSML = "http://www.omg.org/spec/SysML/20161101/SysML";
}
You need to carefully look at your XML content (XML elements
XMI, Documentation, Extension, Elements, Element and
their XML attributes) to see which XML namespace is used where.
Then in all your Java classes you need to specify the right
namespace in the #XmlAttribute and #XmlElement annotations.
You will also need to specify a namespace in the #XmlRootElement
annotation of your root class.
See also the Javadoc of the JAXB annotations.
I will not do all your work for you, but instead only show how
your root class and the Element class may look like.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "XMI", namespace = NameSpaces.XMI)
public class XMI {
#XmlAttribute(name = "version", namespace = NameSpaces.XMI)
private String version;
#XmlElement(name = "Documentation", namespace = NameSpaces.XMI)
private Documentation documentation;
#XmlElement(name = "Extension", namespace = NameSpaces.XMI)
private Extension extension;
// Getters, setters, toString omitted here for brevity
}
#XmlAccessorType(XmlAccessType.FIELD)
public class Element {
#XmlAttribute(name = "idref", namespace = NameSpaces.XMI)
private String idref;
#XmlAttribute(name = "type", namespace = NameSpaces.XMI)
private String type;
#XmlAttribute(name = "name")
private String name;
#XmlAttribute(name = "scope")
private String scope;
// Getters, setters, toString omitted here for brevity
}

Unmarshal element attribute and text to separate fields with jaxb

How do I annotate the externalValue and companyId fields in the Root class so that "abc" gets mapped to externalValue and "123" gets mapped to companyId?
Do I need the #XmlJavaTypeAdapter annotation? Where? I'm hoping that if I do, it can just handle those 2 fields and I can leave the annotations for title and countryCodes as-is.
XML:
<item>
<externalValue companyId="123">abc</externalValue>
<title>My Title</title>
<country>US</country>
<country>CA</country>
</item>
POJO:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Item {
private String externalValue;
private String companyId;
#XmlElement
private String title;
#XmlElement(name = "country")
public List<String> countryCodes;
// getters and setters...
}
I am afraid that this is not possible to achieve only with annotations (so without extra POJO and some adapter) in general case namely JAXB specs. However if your happen to use MOXy as your JAXB implementation it is easy as adding annotation #XmlPath like this:
#XmlPath("externalValue/#companyId")
private String companyId;
Related question: Unmarshalling an XML using Xpath expression and jaxb
You have to define the class in the following manner.
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Item {
private CompanyIdValue companyIdValue;
#XmlElement
private String title;
#XmlElement(name = "country")
public List<String> countryCodes;
//getter and setter
}
In case of both attribute in an XML element tag, you have to define a separate class. Define a separate class called CompanyIdValue, for XML element, you have to define #XmlValue and for attribute you have to annotate with #XmlAttribute
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlValue;
public class CompanyIdValue {
#XmlElement(name = "externalValue")
private String externalValue;
private String companyId;
public String getExternalValue() {
return externalValue;
}
#XmlValue
public void setExternalValue(String externalValue) {
this.externalValue = externalValue;
}
public String getCompanyId() {
return companyId;
}
#XmlAttribute
public void setCompanyId(String companyId) {
this.companyId = companyId;
}
}
I provide below a test program also for testing.
public class Test {
public static void main(String[] args) {
try {
Item item = new Item();
CompanyIdValue companyIdValue = new CompanyIdValue();
companyIdValue.setCompanyId("SomeId");
companyIdValue.setExternalValue("Some External value");
item.setCompanyIdValue(companyIdValue);
item.setCountryCodes(Arrays.asList("A", "B"));
item.setTitle("Some Title");
JAXBContext jaxbContext = JAXBContext.newInstance(Item.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
jaxbMarshaller.marshal(item, System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}

Change Java's output format for nillable XML elements

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.

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 from XML string to Java object, what is going wrong?

I want to create from XML some objects, but when I'm trying I get this error:
[26/07/12 16:20:03:763 CEST] ERROR sitemap.SitemapXMLServlet:
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.sitemaps.org/schemas/sitemap/0.9", local:"urlset"). Expected elements are <{}url>
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.sitemaps.org/schemas/sitemap/0.9", local:"urlset"). Expected elements are <{}url>
I can't find the problem, and I don't understand why it is getting wrong. This is what I tried:
XML File
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://LINK/shop/hoofdcategorie</loc>
<lastmod>2012-07-26</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>
http://LINK/shop/hoofdcategorie/subcategorie
</loc>
<lastmod>2012-07-26</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
</urlset>
Method what unmarshal the XML to an Object
public void getXML() {
final JAXBContext context = JAXBContext.newInstance(SitemapXML.class);
final Unmarshaller unmarshaller = context.createUnmarshaller();
String xml = URLReader.readUrlHttpClient("http://LINK/shop/sitemap.xml");
final UrlSet urlSet = (UrlSet) unmarshaller.unmarshal(new StreamSource(new StringReader(xml)));
}
UrlSet Class
#XmlRootElement(name = "urlset")
public class UrlSet {
#XmlAttribute
String xmlns;
#XmlElement(name = "url")
ArrayList<SitemapXML> sitemaps;
public ArrayList<SitemapXML> getSitemaps() {
return sitemaps;
}
public void setSitemaps(ArrayList<SitemapXML> sitemaps) {
this.sitemaps = sitemaps;
}
#XmlAttribute
public String getXmlns() {
return xmlns;
}
public void setXmlns(String xmlns) {
this.xmlns = xmlns;
}
}
SitemapXML class what I mapping to url
#XmlRootElement(name = "url")
public class SitemapXML {
String loc;
Date lastmod;
String changefreq;
Double priority;
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
public Date getLastmod() {
return lastmod;
}
public void setLastmod(Date lastmod) {
this.lastmod = lastmod;
}
public String getChangefreq() {
return changefreq;
}
public void setChangefreq(String changefreq) {
this.changefreq = changefreq;
}
public Double getPriority() {
return priority;
}
public void setPriority(Double priority) {
this.priority = priority;
}
}
your xml uses a namespace, but your annotations don't mention the namespaces. you can specify the namespace in the XmlRootElement annotation or add an XmlSchema annotation at the package level.
You should add a package-info class with the the #XmlSchema annotation to specify the namespace qualification.
package-info
Below is a sample package-info class with the necessary #XmlSchema annotation. You will need to change the package to match your domain objects.
#XmlSchema(
namespace = "http://www.sitemaps.org/schemas/sitemap/0.9",
elementFormDefault = XmlNsForm.QUALIFIED)
package com.example.foo;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
For More Information
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html

Categories