MOXy JAXB: how to exclude elements from marshalling - jaxb

I have my model:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class CustomerTest {
private Long id;
#XmlPath("contact-info/billing-address")
private AddressTest billingAddress;
#XmlPath("contact-info/shipping-address")
private AddressTest shippingAddress;
#XmlPath("FileHeader/SchemaVersion/text()")
private String schemaVersion;
}
And I fill in the object like this:
private void marshallCustomerTest() {
try {
JAXBContext jc = JAXBContext.newInstance(CustomerTest.class);
CustomerTest customer = new CustomerTest();
customer.setId(new Long(10));
customer.setSchemaVersion("3.2");
AddressTest billingAddress = new AddressTest();
billingAddress.setStreet("1 Billing Street");
customer.setBillingAddress(billingAddress);
AddressTest shippingAddress = new AddressTest();
shippingAddress.setStreet("2 Shipping Road");
customer.setShippingAddress(shippingAddress);
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.marshal(customer, System.out);
} catch (JAXBException jex) {
jex.printStackTrace();
log.error(jex);
}
}
This produce the next XML:
<customerTest xmlns:fe="http://www.facturae.es/Facturae/2009/v3.2/Facturae" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<id>10</id>
<contact-info>
<billing-address>
<street>1 Billing Street</street>
</billing-address>
<shipping-address>
<street>2 Shipping Road</street>
</shipping-address>
</contact-info>
<FileHeader>
<SchemaVersion>3.2</SchemaVersion>
</FileHeader>
</customerTest>
As you can see there is not #XmlPath annotation for 'id' property but this is also present in the final XML. I know I can avoid this behaviour setting the 'id' property to null but I want to know if there is another way. The point is that my real model is much bigger than this one and I would have to set a lot of properties to null.
Any help?
Thanks in advance.

You can either mark the property with #XmlTransient to have it excluded from the XML representation:
#XmlTransient
private Long id;
Or you can annotate your type with #XmlAccessorType(XmlAccessType.NONE) so that only annotated fields/properties are mapped.
#XmlAccessorType(XmlAccessType.NONE)
public class CustomerTest {
For More Information
http://blog.bdoughan.com/2012/04/jaxb-and-unmapped-properties.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.

JAXB unmarshalling doesn't work when field is declared as List but doesn't work when same field is declared as ArrayList

I recently started working on a code which contains some JAXB serializable/deserializable classes. One of the classes has a few lists in it and I wanted to add a new list to it. The lists were initially declared as ArrayList. Similarly, the get methods were also returning ArrayList. I changed all of them to List and added the new list as well as List. But after that, I was not able to unmarshal the xml for this object into a JAVA object. When I change the fields back to ArrayList without any other change, the unmarshalling works fine. I have also tried to attach the DefaultValidationEventHandler to the Unmarshaller but it doesn't spit out any error while unmarshalling. Below is how the class looks like with class and variable names change
#XmlRootElement(name = "commandSet")
public class CommandSet {
private final ArrayList<Command> xCommands;
private final ArrayList<Command> yCommands;
#Override
#XmlElementWrapper(name = "xCommands")
#XmlElement(name = "xCommand", type = Command.class)
public ArrayList<Command> getXCommands() {
return this.xCommands;
}
#Override
#XmlElementWrapper(name = "yCommands")
#XmlElement(name = "yCommand", type = Command.class)
public ArrayList<Command> getYCommands() {
return this.yCommands;
}
}
When xCommands and yCommands are declared as List and the getters also return List, the unmarhsalling doesn't work.
In all the examples that I have found for unmarshalling lists, people have used List<> instead of ArrayList<>. Any ideas why is it not working for me with List<>?
Things I Noticed About Your Code
There are a couple of weird things I notice about your code that may or may not factor into your problem. At the very least I suspect the model you are experiencing the problem with is different from the model you posted in your question.
You have the fields marked final, but never initialize them.
You annotated both of the get methods with #Override but since CommandSet doesn't inherit from anything (other than Object), you aren't overriding anything.
Complete Working Example
Java Model
CommandSet
In this version of the CommandSet class I have made one of the properties type ArrayList and the other of type List to demonstrate that they both work. I have also removed the weird things about your code mentioned above.
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlRootElement
public class CommandSet {
private final ArrayList<Command> xCommands;
private final List<Command> yCommands;
public CommandSet() {
xCommands = new ArrayList<Command>();
yCommands = new ArrayList<Command>();
}
#XmlElementWrapper(name = "xCommands")
#XmlElement(name = "xCommand")
public ArrayList<Command> getXCommands() {
return this.xCommands;
}
#XmlElementWrapper(name = "yCommands")
#XmlElement(name = "yCommand")
public List<Command> getYCommands() {
return this.yCommands;
}
}
Command
public class Command {
}
Demo Code
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(CommandSet.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("input.xml");
CommandSet commandSet = (CommandSet) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(commandSet, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<commandSet>
<xCommands>
<xCommand/>
<xCommand/>
</xCommands>
<yCommands>
<yCommand/>
<yCommand/>
</yCommands>
</commandSet>

JAXB unmarshalling object in object

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

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>

Resources