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>
Related
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.
I have the below JSON code that needs to be parsed. I'm using the corresponding JAX-RS models. The problem is that the paymillClient object is null. If I add currency as a string inside the PaymillSubscription object, it returns EUR value, not null. So there appears to be a problem with the PaymillClient object, not plain strings. Could there be a limit to the number of nested objects for parsing ? Ex, max 2 nested objects. So because there are 3 in my case, it doesn't work.
Unfortunately, I cannot change the JSON code that needs to be parsed at all. I just need to make it work with the JAX-RS implementation.
{
"event":{
"event_type":"subscription.succeeded",
"event_resource":{
"subscription":{
"id":"sub_29f144a3bc32c71f96e2",
"offer":{ },
"livemode":false,
"amount":200,
"temp_amount":null,
"currency":"EUR",
"name":"Monthly subscription",
"interval":"1 MONTH",
"trial_start":null,
"trial_end":null,
"period_of_validity":null,
"end_of_period":null,
"next_capture_at":1428939744,
"created_at":1426264944,
"updated_at":1426264944,
"canceled_at":null,
"payment":{ },
"app_id":null,
"is_canceled":false,
"is_deleted":false,
"status":"active",
"client":{
"id":"client_c0c24aa7f97e1b8ed15d"
}
},
"transaction":{ }
},
"created_at":1426264944,
"app_id":null
}
}
PaymillEventContainer:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class PaymillEventContainer
{
private PaymillEvent event;
}
PaymillEvent:
#XmlAccessorType(XmlAccessType.FIELD)
public class PaymillEvent
{
#XmlElement(name = "event_type") #DocumentationExample(value = "subscription.succeeded") private String eventType;
#XmlElement(name = "event_resource") private PaymillEventResource eventResource;
}
PaymillEventResource:
#XmlAccessorType(XmlAccessType.FIELD)
public class PaymillEventResource
{
private PaymillClient client;
private PaymillOffer offer;
private PaymillSubscription subscription;
}
PaymillSubscription:
#XmlAccessorType(XmlAccessType.FIELD)
public class PaymillSubscription
{
private PaymillClient client;
private PaymillOffer offer;
}
PaymillClient:
#XmlAccessorType(XmlAccessType.FIELD)
public class PaymillClient
{
#DocumentationExample(value = "client_c0c24aa7f97e1b8ed15d") private String id;
}
API endpoint code:
public Response postSubscriptionSucceeded(PaymillEventContainer paymillEventContainer)
{
PaymillEvent paymillEvent = paymillEventContainer.getPaymillEvent();
PaymillEventResource paymillEventResource = paymillEvent.getEventResource();
PaymillSubscription paymillSubscription = paymillEventResource.getSubscription();
PaymillClient paymillClient = paymillSubscription.getPaymillClient();
PaymillOffer paymillOffer = paymillSubscription.getPaymillOffer();
String clientId = paymillClient.getId(); // NullPointerException
}
Ok. I tried to run your code on your machine and also received null (note, that I'm using MOXy to unmarshall JSON). Then, I tried to experiment with it a little and found really funny things:
1. If you will remove all null-valued fields from your JSON, all works just perfect.
2. If you will add another field to PaymillSubscription. I added private Test test, where Test is:
#XmlAccessorType(XmlAccessType.FIELD)
public class Test {
private String id;
}
And will send this "test" object between last null-valued field in subscription object and "client" field:
"test":{"id":"sadas"},
"client":{
"id":"client_c0c24aa7f97e1b8ed15d"
}
Then "test" would be null, but "client" will be parsed as expected.
3. If you will add all null-valued objects into model (I mean, create respective fields in PaymillSubscription class) all works just perfect.
It seems, that by default JAXB specification doesn't allow JSON with unrecognized fields, but MOXy still tries to parse it (and sometimes produces errors).
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
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
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>