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
Related
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>
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>
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>
I'm trying to unmarshall some dozer mapping files in order to provide a mapping availability library to a number of applications. But i cant get the JaxB annotations to work correctly. Either the list of mappings us unmarshalled as null or empty
From the mapping file, all i'm interested in is.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<mappings>
<mapping>
<class-a>package.MySourceClass</class-a>
<class-b>other.package.DestinationClass</class-b>
</mapping>
</mappings>
I have a mappings class
#XmlRootElement(name="mappings")
#XmlAccessorType(XmlAccessType.FIELD)
public class Mappings {
#XmlElementWrapper(name="mappings")
private List<Mapping> mappingEntries = null;
//Getters and setters omitted
and A mapping class
#XmlRootElement(name="mapping")
#XmlAccessorType(XmlAccessType.FIELD)
public class Mapping {
#XmlElement(name ="class-a")
private String classA;
#XmlElement(name = "class-b")
private String classB;
I've tried numerous combinations of the annotations and I cant figure out what i'm doing wrong.
Can someone point me in the right direction.
You could do the following:
Mappings
package forum11193953;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="mappings") // Match the root element "mappings"
#XmlAccessorType(XmlAccessType.FIELD)
public class Mappings {
#XmlElement(name="mapping") // There will be a "mapping" element for each item.
private List<Mapping> mappingEntries = null;
}
Mapping
package forum11193953;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Mapping {
#XmlElement(name ="class-a")
private String classA;
#XmlElement(name = "class-b")
private String classB;
}
Demo
package forum11193953;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Mappings.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml= new File("src/forum11193953/input.xml");
Mappings mappings = (Mappings) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(mappings, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<mappings>
<mapping>
<class-a>package.MySourceClass</class-a>
<class-b>other.package.DestinationClass</class-b>
</mapping>
</mappings>
Try the JMapper Framework: http://code.google.com/p/jmapper-framework/
With JMapper you have all the advantages of dynamic mapping with the performance of static code and much more.
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.