Marshal empty lists as absent nodes with JAXB - java

Using JAXB I would like to have the possibility to marshal empty lists as absent nodes. I think that EclipseLink MOXy has that possibility, but I can't get it to work.
According to: http://wiki.eclipse.org/User:Rick.barkhouse.oracle.com/Test1 you should be able to do it like this:
#XmlElementWrapper(name="line-items", nillable=true)
#XmlNullPolicy(shouldMarshalEmptyCollections=false)
List<LineItem> item = null;
But
shouldMarshalEmptyCollections
is not a valid property.
I've tried using eclipselink 2.4.0, 2.4.1 and 2.5.0-M4. What am I doing wrong?

You could use EclipseLink JAXB (MOXy)'s #XmlPath mapping to map this use case. I'll demonstrate with an example below how it compares to using #XmlElementWrapper.
Root
package forum13268598;
import java.util.List;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlElementWrapper(name="line-items-element-wrapper")
List<LineItem> item1 = null;
#XmlPath("line-items-xml-path/item1")
List<LineItem> item2 = null;
}
jaxb.properties
To use MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
package forum13268598;
import java.util.ArrayList;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Root root = new Root();
root.item1 = new ArrayList<LineItem>();
root.item2 = new ArrayList<LineItem>();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
Output
In the #XmlElementWrapper use case an element is written out for an empty collection, but it is not for the #XmlPath use case.
<?xml version="1.0" encoding="UTF-8"?>
<root>
<line-items-element-wrapper/>
</root>

Related

EclipseLink Moxy unmarshall Collection with different Element names

I have a class which contains an ArrayList(SuperClass) property. Now I wish to unmarshall the following XML file which contains different element names in that collection because these are subclasses of the Superclass. Is there a way of doing this with Moxy?
<?xml version="1.0" encoding="UTF-8"?>
<SmMessageSet xmlns:nav="urn:ccsds:recommendation:navigation:schema:ndmxml:R1.5"
xmlns="urn:ccsds:recommendation:service_management:schema:sccs:R1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:ccsds:recommendation:service_management:schema:sccs:R1.0 file:/C:/CCSDS-910.11-B-1_XML_schemas/CCSDS-910.11-B-1_XML_schemas/SmSchema-v1.0.0.xsd">
<sccsSmVersionRef>sccsSmVersionRef0</sccsSmVersionRef>
<smSource>smSource0</smSource>
<smDestination>smDestination0</smDestination>
<serviceAgreementRef>serviceAgreementRef0</serviceAgreementRef>
<smMessages>
<querySpaceCommunicationServiceProfileFailedReturn>
<messageSequenceNumber>50</messageSequenceNumber>
<messageTimestamp>2006-05-04T18:13:51.0</messageTimestamp>
<invocationMessageSequenceNumber>50</invocationMessageSequenceNumber>
<spaceCommunicationServiceProfileRef>spaceCommunicationServiceProfileRef0
</spaceCommunicationServiceProfileRef>
<qscspError>
<erroredItem>erroredItem0</erroredItem>
<diagnostic>operation timeout</diagnostic>
</qscspError>
<qscspError>
<erroredItem>erroredItem1</erroredItem>
<diagnostic>operation timeout</diagnostic>
</qscspError>
</querySpaceCommunicationServiceProfileFailedReturn>
<createUserAccountInvocation1>
<messageSequenceNumber>50</messageSequenceNumber>
<messageTimestamp>2006-05-04T18:13:51.0</messageTimestamp>
<username>createdUser</username>
<password>createdPassword</password>
<firstname>Test</firstname>
<lastname>User</lastname>
<email>test.user#host.de</email>
<role>SCHEDULING_OFFICER</role>
<superuser>0</superuser>
</createUserAccountInvocation1>
</smMessages>
</SmMessageSet>
The querySpaceCommunicationServiceProfileFailedReturn and createUserAccountInvocation are in my java object model subclasses of SmMessage base class, which is held by the SmMessageSet class in an, as above described, ArrayList of SmMessage classes.
I would also like to not change the current XML structure (i.e. create a wrapper element around the SmMessages in the XML file).
Any help would be appreciated :)
You could do the following leveraging #XmlElementWrapper and #XmlElementRef:
Java Model
SmMessageSet
You can use the #XmlElementWrapper annotation to add a grouping element around the collection (see: http://blog.bdoughan.com/2010/09/jaxb-collection-properties.html). You can also use the #XmlElementRef annotation to model the element name as the inheritance indicator (substitution groups in XML Schema, see: http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-substitution.html).
package forum20745762;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="SmMessageSet")
#XmlAccessorType(XmlAccessType.FIELD)
public class SmMessageSet {
#XmlElementWrapper
#XmlElementRef
private List<SmMessage> smMessages;
}
SmMessage
JAXB/MOXy won't automatically pull in all subclasses of a class, so you can use the #XmlSeeAlso annotation to have them pulled in.
package forum20745762;
import javax.xml.bind.annotation.XmlSeeAlso;
#XmlSeeAlso({CreateUserAccountInvocation.class, QuerySpaceCommunicationServiceProfileFailedReturn1.class})
public class SmMessage {
}
CreateUserAccountInvocation
One each of the subclasses you need to annotate with #XmlRootElement. This is the element name that the #XmlElementRef annotation will match on.
package forum20745762;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class CreateUserAccountInvocation extends SmMessage {
}
QuerySpaceCommunicationServiceProfileFailedReturn1
package forum20745762;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class QuerySpaceCommunicationServiceProfileFailedReturn1 extends SmMessage {
}
package-info
We will use the package level #XmlSchema annotation to map the namespaces (see: http://blog.bdoughan.com/2010/08/jaxb-namespaces.html).
#XmlSchema(
namespace="urn:ccsds:recommendation:service_management:schema:sccs:R1.0",
elementFormDefault=XmlNsForm.QUALIFIED
)
package forum20745762;
import javax.xml.bind.annotation.*;
Demo Code
Demo
The following demo code will read the XML from your question, and then write it back out.
package forum20745762;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(SmMessageSet.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum20745762/input.xml");
SmMessageSet smMessageSet = (SmMessageSet) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(smMessageSet, System.out);
}
}
Output
The output below corresponds to just the subset of your XML document that I had mapped to:
<?xml version="1.0" encoding="UTF-8"?>
<SmMessageSet xmlns="urn:ccsds:recommendation:service_management:schema:sccs:R1.0">
<smMessages>
<querySpaceCommunicationServiceProfileFailedReturn1/>
<createUserAccountInvocation/>
</smMessages>
</SmMessageSet>

MOXy Dynamix JAXB context unmarshalls to wrong property names

I am currently trying to unmarshall the following XML file using the DynamicJaxbContext from MOXy:
<?xml version="1.0" encoding="UTF-8"?>
<request xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://somehost.com/schema_schedule_request.xsd">
<header>
<originator>MySatelliteCompany</originator>
<recipient>Recipient</ recipient>
<schedule_valid_from>2008-12-17T00:00:00Z</schedule_valid_from>
<schedule_valid_to>2008-12-18T00:00:07Z</schedule_valid_to>
<request_reference>HGT4285T3</request_reference>
<generation_time>2008-12-16T08:24:00Z</generation_time>
</header>
<body>
<schedule_request>
<start_time>2008-12-17T09:30:47Z</start_time>
<end_time>2008-12-17T09:33:47Z</end_time>
<satellite_name>MySat</satellite_name>
</schedule_request>
</body>
</request>
It works but the dynamically created Java classes' properties do not correspond to the fields given in the XML. For example: <satellite_name> is unmarshalled to "satelliteName". This makes writing a custom binding file for my backend API quite difficult, because I would have to first either unmarshall all XML files I will get as Input and manually write down the corresponding property names or write another helper app which does this for me.
Is there any way to change this MOXy behavior so it unmarshalls the field names correctly as they are in the XML?
ADDITION:
So I found why this is in the MOXy Documentation:
XML names found in the metadata (complex type names, element names,
attribute names) will be translated to Java identifiers according to
the algorithms described in "Appendix D: Binding XML Names to Java
Identifiers" of the Java Architecture for XML Binding (JAXB) 2.2
Specification (http://jcp.org/en/jsr/detail?id=222). - See more at:
http://www.eclipse.org/eclipselink/documentation/2.4/moxy/dynamic_jaxb001.htm#BABCDJDF
but my principle question still stands: is there any way to shut this off or modify this behavior?
Your ADDITION is correct, MOXy isn't unmarshalling wrong property names, it just unmarshals to property names that correspond to what the mapped property/field names would be in the generated classes.
What the Solution Should Be
binding.xml
The default XML Schema to Java naming rules in JAXB is to remove the _. You can supply a binding file to have this behaviour be different.
<jaxb:bindings
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<jaxb:globalBindings underscoreBinding="asCharInWord"/>
</jaxb:bindings>
Demo
Using MOXy's Dynamic JAXB, below is an example of how you can leverage the bindings file.
import java.io.File;
import java.util.*;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.dynamic.DynamicEntity;
import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContextFactory;
public class Demo {
public static void main(String[] args) throws Exception {
StreamSource schemaSource = new StreamSource("src/forum20146935/schema.xsd");
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(DynamicJAXBContextFactory.EXTERNAL_BINDINGS_KEY, new StreamSource("src/forum20146935/binding.xml"));
JAXBContext jc = DynamicJAXBContextFactory.createContextFromXSD(schemaSource, null, Demo.class.getClassLoader(), properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum20146935/input.xml");
DynamicEntity root = (DynamicEntity) unmarshaller.unmarshal(xml);
System.out.println(root.get("foo_bar"));
}
}
Why Didn't it Work?
As I mentioned earlier MOXy will base the dynamic property name based on the corresponding mapped field/property generated by XJC. This happens to look something like where the property name has the _ but the corresponding mapped field does not.
#XmlElement(name = "foo_bar", required = true)
protected String fooBar;
public String getFoo_Bar() {
return fooBar;
}
public void setFoo_Bar(String value) {
this.fooBar = value;
}
What you Could Do Instead
Use the transformed key name.
You could use the getValueByXPath functionality on the MOXy JAXBContext to interact with the objects in a more XML like way.
import java.io.File;
import java.util.*;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.dynamic.DynamicEntity;
import org.eclipse.persistence.jaxb.JAXBHelper;
import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContextFactory;
public class Demo {
public static void main(String[] args) throws Exception {
StreamSource schemaSource = new StreamSource("src/forum20146935/schema.xsd");
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(DynamicJAXBContextFactory.EXTERNAL_BINDINGS_KEY, new StreamSource("src/forum20146935/binding.xml"));
JAXBContext jc = DynamicJAXBContextFactory.createContextFromXSD(schemaSource, null, Demo.class.getClassLoader(), properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum20146935/input.xml");
DynamicEntity root = (DynamicEntity) unmarshaller.unmarshal(xml);
String fooBar = JAXBHelper.getJAXBContext(jc).getValueByXPath(root, "foo_bar/text()", null, String.class);
System.out.println(fooBar);
}
}

How to make class to be parsable by JAXB?

Hi I need to create following XML using JAXB but since it has many parent-child relationships , I don't want to make as many classes to create that XML.
Anyone can give idea about how I can make this XML with the help of single class...
<Info>
<details>
<arrange>
<name>joseph</name>
<ID>12</ID>
<Date>2012-03-25T11:23:42+10:00</Date>
<LatestDate>
<Start>2012-06-25T09:24:59+10:00</Start>
<End>2013-06-25T09:24:59+10:00</End>
</LatestDate>
<Additional>
<name>IVR</name>
</Additional>
</arrange>
</details>
</Info>
Write an XSD and use JAXB's xjc code generator to create the classes.
#XmlElementWrapper will do the job, you have tor write a single class and define every element with its wrapping element as you can read here: JAXB unmarshalling multiple XML elements into single class
You have to add the needed JAXB-Annotations to your class.
Then you will be able to parse a XML-File and get the Java-Objects.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
Since you are looking to map to the XML with a single class you can use MOXy's #XmlPath extension (see: http://blog.bdoughan.com/2010/07/xpath-based-mapping.html).
Info
import java.util.Calendar;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement(name="Info")
#XmlAccessorType(XmlAccessType.FIELD)
public class Info {
#XmlPath("details/arrange/name/text()")
private String name;
#XmlPath("details/arrange/ID/text()")
private int id;
#XmlPath("details/arrange/Date/text()")
private Calendar date;
#XmlPath("details/arrange/LatestDate/Start/text()")
private Calendar start;
#XmlPath("details/arrange/LatestDate/End/text()")
private Calendar end;
#XmlPath("details/arrange/Additional/name/text()")
private String additionalName;
}
jaxb.properties
To specify MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Info.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum16956564/input.xml");
Info info = (Info) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(info, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8"?>
<Info>
<details>
<arrange>
<name>joseph</name>
<ID>12</ID>
<Date>2012-03-25T11:23:42+10:00</Date>
<LatestDate>
<Start>2012-06-25T09:24:59+10:00</Start>
<End>2013-06-25T09:24:59+10:00</End>
</LatestDate>
<Additional>
<name>IVR</name>
</Additional>
</arrange>
</details>
</Info>

Marshalling the Java objects (without #XmlRootElement) to JSON via Jettison

I have done the marshalling of an JAXB object (Which contains #XmlRootElement) to JSON using Jettison. But I can not convert a simple java object which has no annotations like #XmlRootElement to JSON. I would like to know "Is it mandatory to have that #XmlRootElement to marshall an object to JSON?"
I am getting the following Exception when I try to marshall the java object to Json
com.sun.istack.SAXException2: unable to marshal type "simpleDetail" as an element because it is missing an #XmlRootElement annotation
What could be the issue?
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
The JAXB (JSR-222) specification does not cover JSON-binding. Instead of using a JAXB implementation with the Jettison library, you could use EclipseLink JAXB (MOXy) that offers native JSON-binding. Below is an example.
JAVA MODEL
Foo
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
private List<Bar> mylist;
}
Bar
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Bar {
private int id;
private String name;
}
jaxb.properties
To specify MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html):
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
DEMO CODE
Demo
MOXy does not require the #XmlRootElement annotation, and you can use the JSON_INCLUDE_ROOT property to tell MOXy to ignore the presence of any #XmlRootElement annotations. When the root element is ignored you need to use an unmarshal method that takes a class parameter to specify the type you are unmarshalling.
import java.util.*;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>(2);
properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
JAXBContext jc = JAXBContext.newInstance(new Class[] {Foo.class}, properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StreamSource json = new StreamSource("src/forum15404528/input.json");
Foo foo = unmarshaller.unmarshal(json, Foo.class).getValue();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(foo, System.out);
}
}
input.json/Output
We see that no root element is present in the input or output.
{
"mylist" : [ {
"id" : 104,
"name" : "Only one found"
} ]
}
ADDITIONAL INFORMATION
http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html
http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html
Just read that #XmlRootElement is not necessary always. Please read this blog, at the bottom you will find how it's done without #XmlRootElement.
Also go through the answers in the post No #XmlRootElement generated by JAXB.

Serialize a Java Object

Let me know the best way to serialize my Java object Download. This is a class generated from a java wsimport tool from a WSDL.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Download", propOrder = {
"Response",
"VendorInformation",
"DownloadItem",
"DownloadCommentItem",
"DownloadIntercomItem"
})
public class Download
{
#XmlElement(name = "Response")
protected ResponseMessageManagementType Response;
#XmlElement(name = "VendorInformation")
protected DownloadVendorInformation VendorInformation;
#XmlElement(name = "DownloadItem")
protected List<DownloadDownloadItem> DownloadItem;
#XmlElement(name = "DownloadCommentItem")
protected ArrayOfDownloadDldComment DownloadCommentItem;
#XmlElement(name = "DownloadIntercomItem")
protected ArrayOfDownloadDldIntercom DownloadIntercomItem;
.........................
}
The java classes generated from the tool do not have any serlization implementation.
And I want to serialize the Download class following this kind of format:
<?xml version="1.0" encoding="utf-8"?>
<Download xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="HTTP://xyz.abc.Com//Vendor/DownloadWSE.xsd">
<Response>
.....
</Response>
<VendorInformation>
...............
</VendorInformation>
<DownloadItem>
<DownloadDownloadItem>
.......
</DownloadDownloadItem>
<DownloadDownloadItem>
.......
</DownloadDownloadItem>
<DownloadDownloadItem>
.......
</DownloadDownloadItem>
</DownloadItem>
<DownloadCommentItem>
........
</DownloadCommentItem>
<DownloadIntercomItem>
........
</DownloadIntercomItem>
</Download>
You can see the mapping between XmlElementName and the content of the XML string.
I am at loss on how to do this.
Thanks
This is JAXB. You would need:
JAXBContext ctx = JAXBConetxt.newInstance(Download.class);
Marshaller m = ctx.createMarshaller();
m.marshal(downloadObject, out);
where out can be lots of things, including OutputStream, Writer and File. If you want to get it as a String, use a StringWriter
This is JAXB, and to get your example working you need to supply root element and namespace information:
Root Element
When you marshal an object with JAXB it requires information about the root element. One way to do this is to annotate your Download class with #XmlRootElement
#XmlRootElement(name="Download")
public class Download
If you cannot do that you will need to wrap your instance of Download in a JAXBElement:
Download download = new Download();
QName qname = new QName("HTTP://xyz.abc.Com//Vendor/DownloadWSE.xsd";
JAXBElement<Download> jaxbElement = new JAXBElement(qname, "Download"), Download.class, download);
Namespace Qualification
Also to get the namespace qualification you are after you can use the package level #XmlSchema annotation:
#XmlSchema(
namespace="HTTP://xyz.abc.Com//Vendor/DownloadWSE.xsd",
elementFormDefault=XmlNsForm.QUALIFIED)
package your.model.package.containing.download;
import javax.xml.bind.annotation.*;
Demo
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.namespace.QName;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Download.class);
Download download = new Download();
QName qname = new QName("HTTP://xyz.abc.Com//Vendor/DownloadWSE.xsd";
JAXBElement<Download> jaxbElement = new JAXBElement(qname, "Download"), Download.class, download);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(jaxbElement, System.out);
}
}

Categories

Resources