How to make class to be parsable by JAXB? - java

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>

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

Using JAXB&MOXy to convert JPA entities to XML and back again

I am stuck with (I guess) a pretty trivial problem considering MOXy. Converting a class like this example (pastebin) to XML is no problem, converting it back goes without any errors too. Though, fields that are referencing another (or more) Person, will result in a null value.
Is there any way to make this work without losing relationships? My guess is this is due to a reference by ID only, since JAXB has no way of knowing other existing objects. I have tried using #XmlInverseReference, though this resulted in an infinite loop on every try.
EclipseLink MOXy's #XmlInverseReference is used to solve the infinite loop problem. I will demonstrate below with an example based on your model:
Person
package forum15821738;
import java.io.Serializable;
import java.util.List;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlInverseReference;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Person implements Serializable {
private String name;
#XmlInverseReference(mappedBy="children")
private Person parent;
#XmlElementWrapper
#XmlElement(name="child")
private List<Person> children;
public Person getParent() {
return parent;
}
public List<Person> getChildren() {
return children;
}
// OTHER GETTERS AND SETTERS
}
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
package forum15821738;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Person.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum15821738/input.xml");
Person person = (Person) unmarshaller.unmarshal(xml);
for(Person child : person.getChildren()) {
System.out.println(child.getParent());
}
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(person, System.out);
}
}
Input/Output
forum15821738.Person#5893a012
forum15821738.Person#5893a012
<?xml version="1.0" encoding="UTF-8"?>
<person>
<name>Jane</name>
<children>
<child>
<name>Bobbie</name>
</child>
<child>
<name>Sue</name>
</child>
</children>
</person>
For More Information
http://blog.bdoughan.com/2010/07/jpa-entities-to-xml-bidirectional.html
http://blog.bdoughan.com/2013/03/moxys-xmlinversereference-is-now-truly.html

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>

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