JAXB unmarshalling object in object - jaxb

I cant unmarshall xml because don't understand how to annotate object class in the another object. Please help.
XML:
<?xml version="1.0" encoding="UTF-8"?>
<ODZ xmlns="http://www.company.com/1.0" >
<Data DataID="ZZZ">
<UserData UserKey="user_001">
<UserEvent>...</UserEvent>
</UserData>
</Data>
</ODZ>
Container classes:
I. First level with link to the second (ODZ -> Data).
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name = "ODZ", namespace = "http://www.company.com/1.0")
public class ODZContainer {
private ImportContainer importContainer;
#XmlElement (name = "Data", type=ImportContainer.class)
public ImportContainer getImportContainer() {
return importContainer;
}
}
II. Second level with link to the third level(Data -> UserData).
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name = "Data")
public class ImportContainer {
private String DataID;
private ArrayList<UserDataBean> userDataBean;
#XmlElement (name = "UserData", type=UserDataBean.class)
public ArrayList<UserDataBean> getUserDataBean() {
return userDataBean;
}
#XmlAttribute(name = "DataID")
public String getDataID() {
return DataID;
}
}
III. Third level with link to the fourth level(UserData-> UserEvent).
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name = "UserData")
public class UserDataBean {
private ArrayList<UserEventBean> userEventData;
private String userEventID;
#XmlAttribute(name = "UserKey")
public String getUserEventID() {
return userEventID;
}
#XmlElement (name = "UserEvent", type=UserEventBean.class)
public ArrayList<UserEventBean> getUserEventBean() {
return userEventData;
}
}

The namespace qualification in your JAXB metadata does not match your XML. You can use the package level #XmlSchema annotation to specify the namespace qualification for your model.
#XmlSchema(
namespace = "http://www.company.com/1.0",
elementFormDefault = XmlNsForm.QUALIFIED)
package example;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
For More Information on JAXB and Namespaces
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
Notes About Your Metadata
Since the type of the ArrayList is already specified, you don't need to specify it via the #XmlElement annotation. It doesn't hurt, but its not necessary.
#XmlElement (name = "UserData", type=UserDataBean.class)
public ArrayList<UserDataBean> getUserDataBean() {
return userDataBean;
}
#XmlAccessorType(XmlAccessType.NONE) means that nothing is mapped unless it is explicitly annotated. This may or not be what you want. You may find the following article useful:
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html

Related

Ignore xml element while marshalling and include xml element while unmarsalling by using XmlTransient annotation

I am trying to unmarshal an XML into an object that I expect should have a certain field. However, I do not want to marshal that object into an XML that contains it. What I like would be similar to this:
#XmlRootElement(name = "User")
public class User {
#XmlElement(namespace = "http://www.........", required = false)
private String name;
}
While marshalling , am getting below xml,
Current xml output file looks like:
<User xmlns:ns5="http://www......." />
But I don't want this namespace in output file,
Expected looks like below xml output file,
<User />
I tried to use #XmlTransient annotation,
#XmlRootElement(name = "User")
public class User {
#XmlTransient
private String name;
}
Worked successfully while marshalling and got exception while unmarshalling
org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: No descriptor found while unmarshalling element mapped to user name
Any help would be greatly appreciated.
As per my understanding, you would not want to get the namespaces in your XML. Based on my understanding providing the answer here. Hope it helps or at least guides you in the right direction.
I have done something like this:
import jakarta.xml.bind.Marshaller;
import jakarta.xml.bind.Unmarshaller;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlTransient;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
#AllArgsConstructor
#XmlRootElement(name = "User")
public class User {
#XmlTransient
#XmlElement(namespace = "http://www.google/test.com", required = false)
private String name;
private String name1;
public void beforeMarshal(Marshaller m) {
//Executed after reading the data but before marshalling to XML. Here you can perform whatever you would like.
name1 = name;
name = null;
}
public void afterUnmarshal(Unmarshaller m, Object parent) {
//Executed after Unmarshalling the data. Here you can perform whatever you would like.
}
}
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Marshaller;
import javax.xml.stream.XMLStreamException;
public class Main {
public static void main(String[] args) throws JAXBException, XMLStreamException {
User user = new User();
user.setName("Batman");
final JAXBContext jaxbContext = JAXBContext.newInstance(User.class);
final Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(user, System.out);
}
}
This would result in following XML:
<?xml version="1.0" encoding="UTF-8"?>
<User>
<name1>Batman</name1>
</User>
You can perform accordingly whatever you need within the beforeMarshal and afterMarshal if required.

How to solve the JAXB IllegalAnnotationException error

I am trying to consume a web service using JAX-WS, and it throws me the following error corresponding to the following classes:
Class 1
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "validarComprobante", propOrder = { "xml" })
public class ValidarComprobante {
protected byte[] xml;
public byte[] getXml() {
return xml;
}
public void setXml(byte[] value) {
this.xml = value;
}
}
Class 2
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "validarComprobanteResponse", propOrder = { "respuestaRecepcionComprobante" })
public class ValidarComprobanteResponse {
#XmlElement(name = "RespuestaRecepcionComprobante")
protected RespuestaSolicitud respuestaRecepcionComprobante;
public RespuestaSolicitud getRespuestaRecepcionComprobante() {
return respuestaRecepcionComprobante;
}
public void setRespuestaRecepcionComprobante(RespuestaSolicitud value) {
this.respuestaRecepcionComprobante = value;
}
}
These classes were generated with wsimport.exe which is inside the Java 8 SDK. The error is as follows:
com.sun.xml.ws.spi.db.DatabindingException: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions
Two classes have the same XML type name "{http://ec.gob.sri.ws.recepcion}validarComprobante". Use #XmlType.name and #XmlType.namespace to assign different names to them.
this problem is related to the following location:
at com.horus.microservices.ebillinginvoice.sri.recepcion.ValidarComprobante
at public javax.xml.bind.JAXBElement com.horus.microservices.ebillinginvoice.sri.recepcion.ObjectFactory.createValidarComprobante(com.horus.microservices.ebillinginvoice.sri.recepcion.ValidarComprobante)
at com.horus.microservices.ebillinginvoice.sri.recepcion.ObjectFactory
this problem is related to the following location:
at recepcion.ws.sri.gob.ec.ValidarComprobante
Two classes have the same XML type name "{http://ec.gob.sri.ws.recepcion}validarComprobanteResponse". Use #XmlType.name and #XmlType.namespace to assign different names to them.
this problem is related to the following location:
at com.horus.microservices.ebillinginvoice.sri.recepcion.ValidarComprobanteResp
at public com.horus.microservices.ebillinginvoice.sri.recepcion.ValidarComprobanteResp com.horus.microservices.ebillinginvoice.sri.recepcion.ObjectFactory.createValidarComprobanteResponse()
at com.horus.microservices.ebillinginvoice.sri.recepcion.ObjectFactory
this problem is related to the following location:
at recepcion.ws.sri.gob.ec.ValidarComprobanteResponse
at com.sun.xml.ws.db.glassfish.JAXBRIContextFactory.newContext(JAXBRIContextFactory.java:105)
at
Please any help
Thanks

JAXB ignore fields from processing

I have the following class
#XmlRootElement
public class Test {
#XmlTransient
private String abc;
#XmlElement
private String def;
}
My question is, I want to use this class to generate two kinds of XMLs
1. With <abc>
2. without <abc>
I can achieve the second one since I have marked it as transient. Is there any way so that if I mark "abc" as #XMLElement and can ignore it while marshalling?
Thanks in advance
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
You may be interested in the #XmlNamedObjectGraph extension we have added in EclipseLink 2.5.0. It allows you to define multiple views on your domain model. You can try this out today using a nightly build:
http://www.eclipse.org/eclipselink/downloads/nightly.php
Below I'll give an example:
Test
The #XmlNamedObjectGraph annotation is used to define subsets of the object graph that can be used when marshalling and unmarshalling.
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;
#XmlNamedObjectGraph(
name="only def",
attributeNodes = {
#XmlNamedAttributeNode("def")
}
)
#XmlRootElement
public class Test {
private String abc;
private String def;
public String getAbc() {
return abc;
}
public void setAbc(String abc) {
this.abc = abc;
}
public String getDef() {
return def;
}
public void setDef(String def) {
this.def = def;
}
}
Demo
The MarshallerProperties.OBJECT_GRAPH can be used to specify which object graph should be marshallled.
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.MarshallerProperties;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Test.class);
Test test = new Test();
test.setAbc("FOO");
test.setDef("BAR");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// Marshal the Entire Object
marshaller.marshal(test, System.out);
// Marshal Only What is Specified in the Object Graph
marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, "only def");
marshaller.marshal(test, System.out);
}
}
Output
Below is the output from running the demo code. The first time the instance of Test is marshalled it contains all the properties and the second time just the def property.
<?xml version="1.0" encoding="UTF-8"?>
<test>
<abc>FOO</abc>
<def>BAR</def>
</test>
<?xml version="1.0" encoding="UTF-8"?>
<test>
<def>BAR</def>
</test>
For More Information
http://blog.bdoughan.com/2013/03/moxys-object-graphs-inputoutput-partial.html
http://blog.bdoughan.com/2013/03/moxys-object-graphs-partial-models-on.html

Output empty elements

I have a Object with two fields "name" and "address". JAXB ignores the empty elements while transforming the object into XMl.
For ex: if I have name="xyz" and address=null then out will be
<name>xyz</name>
but what I want as an output as
<name>xyz</name>
<address></address>
I have seen the option #XmlElement(nillable="true") but this gives the output as
<name>xyz</name>
<address xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
Please help me getting the desired output.
Thanks in advance.
A JAXB (JSR-222) implementation will output an empty String "" value as an empty element. You can set the address property to this to get the desired effect.
UPDATE #1
I have updated my question. Basically the address element is NULL. Is
this solution applicable to that as well?
You could leverage Marshal Event Callbacks to adjust the value of address.
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
private String name;
private String address;
private void beforeMarshal(Marshaller marshaller) {
if(null == address) {
address = "";
}
}
private void afterMarshal(Marshaller marshaller) {
if("".equals(address)) {
address = null;
}
}
}
UPDATE #2
The only concern is that if I have 10 fields in the class I will have
to write if for all the fields. Is there any other solution?
If you use EclipseLink MOXy as your JAXB provider (I'm the MOXy lead), then you could use an XmlAdapter for this use case.
XmlAdapter (StringAdapter)
package forum14691333;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class StringAdapter extends XmlAdapter<String, String> {
#Override
public String marshal(String string) throws Exception {
if(null == string) {
return "";
}
return string;
}
#Override
public String unmarshal(String string) throws Exception {
if("".equals(string)) {
return null;
}
return string;
}
}
package-info
Then if you specify it at the package level it will apply to all mapped fields/properties of type String within that package.
#XmlJavaTypeAdapter(value=StringAdapter.class, type=String.class)
package forum14691333;
import javax.xml.bind.annotation.adapters.*;
For More Information
http://blog.bdoughan.com/2012/02/jaxb-and-package-level-xmladapters.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
If you use EclipseLink MOXy as your JAXB provider then you could use
#XmlNullPolicy(emptyNodeRepresentsNull = true, nullRepresentationForXml = XmlMarshalNullRepresentation.EMPTY_NODE)
#XmlElement(name = "address", nillable = true)
private String address;
By using this way, you don't have to write adapter for all the fields
Simply set an empty string default value on the field.
#XmlElement(required="true")
private String address = "";
and you will get
<address></address>

JAXB default behavior for attributes

in JAXB, if annotations were not provided, the element names will be derived from the property names not fields, but what about attributes in this case? is there any default behavior for writing out attributes back to the XML file?
1) If annotations were not provided:
Every public getter/setter pair and every public field will be
automatically bound to XML, unless annotated by {#link XmlTransient}
For example
public class Cat
{
public String name = "tomcat";
private String nick = "catalina";
public int getAge() { return 5; }
public void setAge(int age) {}
}
after
JAXB.marshal(cat, System.out);
output is
<cat>
<name>tomcat</name>
<age>5</age>
</cat>
2) What about XML attributes? XML attribute is named like field or getter/setter pair and placed in the root node
for example
#XmlAccessorType(XmlAccessType.FIELD)
public class Cat
{
String name = "tomcat";
#XmlAttribute
String nick = "catalina";
#XmlAttribute
String home = "java.home";
int age = 5;
}
output is
<cat home="java.home" nick="catalina">
<name>tomcat</name>
<age>5</age>
</cat>

Resources