Override JAXB binding in non-modifyable domain Java classes - java

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

Related

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

Jaxb marshalling with custom annotations

I have a requirement, to marshall/unmarshall some elements of java pojo depending upon a custom annotation marked on the field.
suppose there are 3 fields in my java pojp
#CustomVersion("v1")
private String field1;
#CustomVersion("v1","v2")
private String field2;
#CustomVersion("v2")
private String field3;
i would like to marshall only the fields with v1 if i pass version="v1" parameter while conversion in jaxb. if i pass v2, all fields with v2 annotation should only be marshalled.
is that even possible using jaxb? i am sure selective marshalling would be supported through some library or way, am not still able to figure it out after quite some searching.
any help or advice or pointers are highly appreciated.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
Below is an example of how you could use MOXy's #XmlNamedObjectGraphs extension to map your use case.
Java Model
Foo
The #XmlNamedObjectGraphs extension allows you to specify multiple subsets of mappings identified by a key.
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlNamedAttributeNode;
import org.eclipse.persistence.oxm.annotations.XmlNamedObjectGraph;
import org.eclipse.persistence.oxm.annotations.XmlNamedObjectGraphs;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlNamedObjectGraphs({
#XmlNamedObjectGraph(
name="v1",
attributeNodes = {
#XmlNamedAttributeNode("field1"),
#XmlNamedAttributeNode("field2")}),
#XmlNamedObjectGraph(
name="v2",
attributeNodes = {
#XmlNamedAttributeNode("field2"),
#XmlNamedAttributeNode("field3")})
})
public class Foo {
private String field1 = "ONE";
private String field2 = "TWO";
private String field3 = "THREE";
}
jaxb.properties
To use MOXy as your JAXB provider you need to include a file called jaxb.properties 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
You can specify the key corresponding to the object graph to have that subset applied to the object you are marshalling.
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import org.eclipse.persistence.jaxb.MarshallerProperties;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Foo foo = new Foo();
// Marshal Everything
marshaller.marshal(foo, System.out);
// Marshal "v1" Data
marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, "v1");
marshaller.marshal(foo, System.out);
// Marshal "v2" Data
marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, "v2");
marshaller.marshal(foo, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<field1>ONE</field1>
<field2>TWO</field2>
<field3>THREE</field3>
</foo>
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<field1>ONE</field1>
<field2>TWO</field2>
</foo>
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<field2>TWO</field2>
<field3>THREE</field3>
</foo>
For More Information
http://blog.bdoughan.com/2013/03/moxys-object-graphs-inputoutput-partial.html
First of all I would suggest doing such preprocessing before marshalling. It would be much easier. However if it is not possible for some reason then you can create you custom type adapter. Then you can put #XmlJavaTypeAdapter(VersioningAdapter.class) on every type that you want to have versioning enabled.
#XmlJavaTypeAdapter can also be specified on package level, but you have to specify to which types it applies. You cannot use XmlAdapter without specifying somewhere #XmlJavaTypeAdapter.
Drawbacks of such solution:
if you have multiple versioned types then each of them has to be annotated with #XmlJavaTypeAdapter
#XmlJavaTypeAdapter does not work for root element, only on child elements. You have to call adapter manually on root element before marshalling
AFAIK there is no other option for customizing JAXB marshalling. That's why I think that annotation processing should be performed in separate step before marshalling. Unless you can accept mentioned limitations.
Sample adapter (full code can be found here):
public class VersioningAdapter extends XmlAdapter<Object, Object> {
#Override
public Object unmarshal(Object v) throws Exception {
// TODO Auto-generated method stub
return null;
}
#Override
public Object marshal(Object v) throws Exception {
if (v == null) {
return v;
}
Field[] fields = v.getClass().getDeclaredFields();
for (Field field : fields) {
Annotation[] annotations = field.getDeclaredAnnotations();
CustomVersion annotation = findCustomVersion(annotations);
if (annotation != null) {
if (!contains(annotation, Configuration.getVersion())) {
field.setAccessible(true);
field.set(v, null);
}
}
}
return v;
}
private CustomVersion findCustomVersion(Annotation[] annotations) {
for (Annotation annotation : annotations) {
if (annotation instanceof CustomVersion) {
return (CustomVersion) annotation;
}
}
return null;
}
private boolean contains(CustomVersion annotation, String version) {
String[] values = annotation.value();
for (String value : values) {
if (value.equals(version)) {
return true;
}
}
return false;
}
}

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>

Can I get MOXy to convert a string to a boolean when generating json

The object model has an element ended of type String
public class LifeSpan {
protected String begin;
protected String end;
#XmlJavaTypeAdapter(CollapsedStringAdapter.class)
protected String ended;
....
but its actually only ever a boolean, (I dont know the significance of the XmlJavaTypeAdapter annotation)
When output as XML gives
<life-span><begin>1999-04</begin><ended>true</ended></life-span>
so doesn't really matter if defined as boolean or string
but JSON output is
"life-span" : {
"begin" : "1999-04",
"ended" : "true"
},
when I need it to be
"life-span" : {
"begin" : "1999-04",
"ended" : true
},
I cannot really change the object model so thought I might be able to map to the correct type in oxml.xml file , and tried
<java-type name="LifeSpan">
<java-attributes>
<xml-element java-attribute="ended" type="boolean"/>
</java-attributes>
</java-type>
but it didnt like that.
Below is how you can support this use case with EclipseLink JAXB (MOXy):
BooleanStringAdapter
An XmlAdapter allows you to marshal one type of object as another type (see http://blog.bdoughan.com/2010/07/xmladapter-jaxbs-secret-weapon.html). In this example we want to treat the String value as a Boolean one.
package forum11451880;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class BooleanStringAdapter extends XmlAdapter<Boolean, String> {
#Override
public String unmarshal(Boolean v) throws Exception {
return v.toString();
}
#Override
public Boolean marshal(String v) throws Exception {
return Boolean.valueOf(v);
}
}
oxm.xml
We can leverage MOXy's external mapping document to augment the metadata supplied via annotations to hook in our XmlAadapter (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="forum11451880">
<java-types>
<java-type name="LifeSpan">
<java-attributes>
<xml-element java-attribute="end">
<xml-java-type-adapter value="forum11451880.BooleanStringAdapter"/>
</xml-element>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
LifeSpan
Below is your domain model with an end property of type String.
package forum11451880;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class LifeSpan {
protected String begin;
protected String end;
#XmlJavaTypeAdapter(CollapsedStringAdapter.class)
protected String ended;
}
jaxb.properties
To specify MOXy as your JAXB provider you need to include a file called jaxb.properties with the followin 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 the external mapping document.
package forum11451880;
import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
import org.eclipse.persistence.jaxb.MarshallerProperties;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, "forum11451880/oxm.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {LifeSpan.class}, properties);
LifeSpan lifeSpan = new LifeSpan();
lifeSpan.begin = "1999-04";
lifeSpan.end = "true";
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(lifeSpan, System.out);
marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
marshaller.marshal(lifeSpan, System.out);
}
}
Output
Below is the output from running the example. As you can see the true value appears without quotes:
<?xml version="1.0" encoding="UTF-8"?>
<lifeSpan>
<begin>1999-04</begin>
<end>true</end>
</lifeSpan>
{
"lifeSpan" : {
"begin" : "1999-04",
"end" : true
}
}

How can I marshall a single java bean into a complex XML document with existing annotations?

I have a single java bean, which is already annotated for JPA, that I also wish to store as XML, specifically FIXML. The goal is to manage the mapping from bean to XML with annotations.
I see related topics online about specifying a schema and letting JAXB generate classes, but I don't want to do that.
I've been looking at using JAXB annotations, but it seems that I'll need to make new classes for each child element. I'm trying to stay away from that, and let the annotations show how to construct the child elements. JAXB does not seem to want to do this.
Is this possible, and how? Do I need to make my own annotations and forget about JAXB?
Concrete example
Bean:
#Entity
#XmlRootElement(name="FIXML")
#XmlType(name="ExecRpt")
public class ExecutionReport implements Serializable {
private String account;
private String senderCompID;
#Column(name="ACCOUNT", nullable=true, length=64)
#XmlAttribute(name="Acct")
public String getAccount() {
return this.account;
}
public void setAccount(String account) {
this.account = account;
}
#Column(name="SENDER_COMP_ID", nullable=true, length=200)
#XmlAttribute(name="SID")
public String getSenderCompID() {
return this.senderCompID;
}
public void setSenderCompID(String senderCompID) {
this.senderCompID = senderCompID;
}
}
Parsing:
JAXBContext context = JAXBContext.newInstance(ExecutionReport.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); //pretty print XML
marshaller.marshal(executionReport, System.out);
Desired resulting XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<FIXML>
<ExecRpt Acct="account_data">
<Hdr SID="sender"/>
</ExecRpt>
</FIXML>
Current resulting XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<FIXML Acct="account_data" SID="sender"/>
Clearly I'm not providing enough information to map the child elements yet, but I'm also not sure how to provide it. I want to add some #XmlElement annotations, but I don't have child objects, all the data is in this class.
The upside is that my XML isn't much more complicated than this example; there are only a handful of elements, and they only appear once per message. The thing that's giving me trouble is getting multiple elements out of a single bean.
You can use the #XmlPath extension in EclipseLink JAXB (MOXy) for this, I'm the tech lead.
Model Class
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#Entity
#XmlRootElement(name="FIXML")
#XmlType(name="ExecRpt")
public class ExecutionReport implements Serializable {
private String account;
private String senderCompID;
#Column(name="ACCOUNT", nullable=true, length=64)
#XmlPath("ExecRpt/#Acct")
public String getAccount() {
return this.account;
}
public void setAccount(String account) {
this.account = account;
}
#Column(name="SENDER_COMP_ID", nullable=true, length=200)
#XmlPath("ExecRpt/Hdr/#SID")
public String getSenderCompID() {
return this.senderCompID;
}
public void setSenderCompID(String senderCompID) {
this.senderCompID = senderCompID;
}
}
Demo Code
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(ExecutionReport.class);
ExecutionReport er = new ExecutionReport();
er.setAccount("account_data");
er.setSenderCompID("sender");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(er, System.out);
}
}
Resulting XML
<?xml version="1.0" encoding="UTF-8"?>
<FIXML>
<ExecRpt Acct="account_data">
<Hdr SID="sender"/>
</ExecRpt>
</FIXML>
Specifying the EclipseLink JAXB (MOXy) Implementation
To specify MOXy as the JAXB implementation you need to add a file called jaxb.properties in with your ExecutionReport class with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
For More Information
http://bdoughan.blogspot.com/2010/09/xpath-based-mapping-geocode-example.html
I don't know Jaxb annotations but if you ask for an attribute in ExecRpt it seems normal to have an attribute in ExecRpt no?
I think you expect a bit too much of these annotations. Don't you also want an annotation that would take a string, split it with a separator and generate a list of child elements or something?
And it seems to me a bad design to put these annotations directly on JPA entities. One day you could have to do some database changes for performances issues for exemple and you could not be able to generate the xml you want anymore. Why not transforming your jpa entity to a given structure jaxb friendly so that you keep the db and marshalling appart? Thus on change you would just have to modify the transformer.

Categories

Resources