MOXy Dynamix JAXB context unmarshalls to wrong property names - java

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);
}
}

Related

JAXB Mapping to JSON

I have written a JAX-RS (Jersey) REST Service, which accepts XML messages of ONIX XML format. Generally, I have generated all the required classes for JAXB binding from the given schema with xjc. There are more than 500 classes overall and I cannot modify them.
Now, when I have a JAXB-mapped object, I need to store it to the database. I work with mongoDb, so the message format should be JSON. I tried to use Jackson with JAXB module to convert JAXB-object into JSON, which works pretty fine with storing the data. But when I try to convert the JSON back into the JAXB object, it throws an exception connected somehow with the JAXBElement. In google I found out that the JAXBElement is not supported in Jackson and I have to work around this issue. But I cant do it because I cannot modify JAXB-generated classes.
Is there a way to map JAXB Objects into JSON with some other means, but which will follow the whole JAXB specification so that I have no problems in the future converting from JSON to the JAXB object and visa vera?
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
If you can't do it with Jackson, you use case will work with MOXy.
Java Model
Foo
Here is a sample class that contains a field of type JAXBElement.
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
#XmlElementRef(name="bar")
private JAXBElement<Bar> bar;
}
Bar
public class Bar {
}
ObjectFactory
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
#XmlElementDecl(name = "bar")
public JAXBElement<Bar> createBar(Bar bar) {
return new JAXBElement<Bar>(new QName("bar"), Bar.class, bar);
}
}
Standalone Demo Code
Below is some demo code you can run in Java SE to see that everything works:
Demo
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, ObjectFactory.class}, properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StreamSource json = new StreamSource("src/forum19158056/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
{"bar" : {} }
Running with JAX-RS
The following links will help you leverage MOXy in a JAX-RS service:
http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html
http://blog.bdoughan.com/2013/06/moxy-is-new-default-json-binding.html
Since you're using Jackson you can construct an ObjectMapper with the JaxbAnnotationModule and write out the value. The following is some code to write a JAXB annotated object to system out.
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JaxbAnnotationModule());
mapper.writeValue(System.out, jaxbObject);
This approach will also work on Glassfish as it does not utilize the provider given by the container which would cause ClassNotFoundException as per GLASSFISH-21141

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.

Override JAXB binding in non-modifyable domain Java classes

I have spent the whole day trying to figure out this problem (including extensive searching on this site), but I can't find an answer to my problem. I am trying to achieve this:
Convert between XML and some existing Java objects that I have no control over
Names of elements in the resulting/source XML differ from names of properties of the Java classes
I am limited to jaxb-2.0
I may introduce a wrapping class that can contain the annotations
Let me show you an example of what I'm trying to achieve. Let's assume that the Java class I have no control over looks like this:
public class TopNoControlClass {
private BottomNoControlClass bottomNoControlObject;
public TopNoControlClass(BottomNoControlClass bottomNoControlObject) {
super();
this.bottomNoControlObject = bottomNoControlObject;
}
public BottomNoControlClass getBottomNoControlObject() {
return bottomNoControlObject;
}
public void setBottomNoControlObject(BottomNoControlClass bottomNoControlObject) {
this.bottomNoControlObject = bottomNoControlObject;
}
}
And the referenced class:
public class BottomNoControlClass {
private String foo;
public BottomNoControlClass(String foo) {
super();
this.foo = foo;
}
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
}
And imagine I want to get this out of the marshalling:
<?xml version="1.0" encoding="UTF-8"?>
<Top>
<Bottom>
<bar>XXX</bar>
</Bottom>
</Top>
The <Top> would map to the TopNoControlClass and <Bottom> Bottom would map to the BottomNoControlClass and <bar> would map to the foo property of BottomNoControlClass.
In order to do the above, I would be comfortable with creating an external XML binding that would state the mappings, but I can't figure a way to use that external binding file in runtime. All the examples I've seen so far only used external XML bindings at generation time (i.e. as a parameter to xjc).
I also wouldn't have a problem with introducing a wrapper class that would override the class names and class property names for the classes it would refer (i.e. TopNoControlClass and BottomNoControlClass). It would be easy to construct the JAXBContext with that class and let JAXB do the rest. But I can't figure out how that annotation should look like.
Any help would be greatly appreciated
Jaroslav
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
In the MOXy implementation of JAXB 2 (JSR-222) we offer an external mapping document for exactly this use case.
External Metadata (oxm.xml)
Below is what the mapping document would look like for your use case. For the purpose of this example put your model classes in a package called forum13318677. For more information see: http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="forum13318677">
<java-types>
<java-type name="TopNoControlClass">
<xml-root-element name="Top"/>
<xml-type factory-class="forum13318677.Factory" factory-method="createTopNoControlClass"/>
<java-attributes>
<xml-element java-attribute="bottomNoControlObject" name="Bottom"/>
</java-attributes>
</java-type>
<java-type name="BottomNoControlClass">
<xml-type factory-class="forum13318677.Factory" factory-method="createBottomNoControlClass"/>
<java-attributes>
<xml-element java-attribute="foo" name="bar"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
Factory
Since your model classes did not have 0-argument constructors I needed to create a factory class (see: http://blog.bdoughan.com/2011/06/jaxb-and-factory-methods.html). This class was configured in the XML representation of the #XmlType annotation.
package forum13318677;
public class Factory {
public TopNoControlClass createTopNoControlClass() {
return new TopNoControlClass(null);
}
public BottomNoControlClass createBottomNoControlClass() {
return new BottomNoControlClass(null);
}
}
jaxb.properties
To specify MOXy as your JAXB (JSR-222) provider you need to include a file called jaxb.properties in the same package as your domain classes 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
The demo code below demonstrates how to leverage MOXy's external mapping document. Note that even though MOXy is used as the JAXB provider there are no compile time dependencies on MOXy.
package forum13318677;
import java.io.File;
import java.util.*;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String,Object>();
properties.put("eclipselink.oxm.metadata-source", "forum13318677/oxm.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {TopNoControlClass.class}, properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum13318677/input.xml");
TopNoControlClass object = (TopNoControlClass) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(object, System.out);
}
}

Marshal empty lists as absent nodes with JAXB

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>

Categories

Resources