Mapping XML Entities to Java Objects - java

I am quite sure, this is one of the many duplicated questions around XML to Java Object conversions.
But I started this thread since I couldn't find simpler or looking for simpler solution.
I have an xsd [Infact I am designing it] and xml.
I would like to auto-map the xml data to Java beans as per mapping
<tns:SummaryCart xmlns:tns="SummaryCart" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="SummaryCart.xsd">
<SummaryElement type="test">
<order>1</order>
<id>A</id>
<displayName>A</displayName>
<subElements>
<order>1</order>
<id>Preactivation</id>
<displayName>Preactivation</displayName>
</subElements>
<maxlines>1</maxlines>
</SummaryElement>
</tns:SummaryCart>
Now my Java classes will be
public class SummaryCart{
private List<SummaryElement> summaryElementList;
}
public class SummaryElement {
private int order;
private String id;
private String displayName;
private String property;
private List<SummaryElement> subElements;
private int maxlines;
private String type;
}
Is there any simple tool/framework which can auto-map the data from XML to Java beans [MUST support attributes/element mapping]. Tutorial will be good.
Btw, I am using Spring framework, if spring-oxm advantage is taken, its welcome.

Below is how you could map your object to XML using JAXB (JSR-222). An implementation is included in the JDK/JRE starting with Java SE 6. JAXB is supported by Spring (see section 8.5: http://static.springsource.org/spring-ws/site/reference/html/oxm.html)
SummaryCart
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="SummaryCart", namespace="SummaryCart")
#XmlAccessorType(XmlAccessType.FIELD)
public class SummaryCart{
#XmlElement(name="SummaryElement")
private List<SummaryElement> summaryElementList;
}
SummaryElement
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class SummaryElement {
private int order;
private String id;
private String displayName;
private String property;
private List<SummaryElement> subElements;
private int maxlines;
#XmlAttribute
private String type;
}
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(SummaryCart.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum15881876/input.xml");
SummaryCart sc = (SummaryCart) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "SummaryCart.xsd");
marshaller.marshal(sc, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:SummaryCart xmlns:ns2="SummaryCart" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="SummaryCart.xsd">
<SummaryElement type="test">
<order>1</order>
<id>A</id>
<displayName>A</displayName>
<subElements>
<order>1</order>
<id>Preactivation</id>
<displayName>Preactivation</displayName>
<maxlines>0</maxlines>
</subElements>
<maxlines>1</maxlines>
</SummaryElement>
</ns2:SummaryCart>

Basically you want to unmarshal your XML. Here's a detailed tutorial that describes how to use the JAXB xjc command to generate a Java class from XML Schema. A maven xjc plugin is also available for your convenience.

Related

any way to convert xml to object in java?

I am trying to use a XML and access to all fields and data on an easy way, so, I decided to use JaxB , but I have no idea how to create all the classes for the objects, I tried manually like this.
#XmlRootElement(name = "Response")
public class Response {
#XmlElement(ns = "SignatureValue")
String signatureValue;
}
But I get an error on #XmlElement saying the annotation is disallowed for this location...
I checked other posts and they work great if I have something like Hellw but doesnt work with more complex formats, an example of first part of mine is like this
<?xml version="1.0" encoding="UTF-8"?><DTE xsi:noNamespaceSchemaLocation="http://www.myurl/.xsd" xmlns:gs1="urn:ean.ucc:pay:2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
any idea how to do all this??
Thanks in advance
EDIT:
I forgot to say, the XML is actually a String with the entire XML.
The #XmlElement annotation is valid on a field. If you have a corresponding property then you should annotate the class with #XmlAccessorType(XmlAccessType.FIELD) to avoid a duplicate mapping exception.
Java Model
Annotating the Field
#XmlRootElement(name = "Response")
#XmlAccessorType(XmlAccessType.FIELD)
public class Response {
#XmlElement(name = "SignatureValue")
String signatureValue;
public String getSignatureValue() {
return signatureValue;
}
public void setSignatureValue(String signatureValue) {
this.signatureValue = signatureValue;
}
}
Annotating the Property
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "Response")
public class Response {
String signatureValue;
#XmlElement(name = "SignatureValue")
public String getSignatureValue() {
return signatureValue;
}
public void setSignatureValue(String signatureValue) {
this.signatureValue = signatureValue;
}
}
For More Information
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
Demo Code
Below is some demo code that reads/writes the XML corresponding to your Response class.
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Response.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum19713886/input.xml");
Response response = (Response) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(response, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<SignatureValue>Hello World</SignatureValue>
</Response>

Dynamic java from XML

I would like to build some kind of object generation engine for my domain objects.
For example, lets assume, I'm working with graphs. The models are represented by xml and I should be able to load them and build a java representation at runtime.
Lets say, graph has vertices and edges
So it will look like this:
<graph>
<vertex id="n1" color="red", thickness="2">
<vertex id="n2">
<edge end1="${n1}", end2="${n2}"/>
</graph>
For this I would like to get the objects that can be described by the following java classes:
class Graph {
List<Vertex> vertexList
List<Edge> edgeList
}
class Vertex {
String id
... various properties ...
}
class Edge {
Vertex end1
Vertex end2
}
Another requirement is to be able to define vertices in loop like this:
<graph>
...
<for var = i, min = 1, max = 10, step = 1>
<vertex id=$i.../>
</for>
...
</graph>
I thought about using Apache Jelly but it seems to be a 'dead' project, JaxB doesn't allow such a level of dynamic behavior AFAIK...
My question is - what framework can you recommend for implementing such a task?
If there is something that works like Apache Jelly but still maintained, it could be great also :)
Thanks a lot in advance
JAXB (JSR-222) implementations can easily handle references within a document using a combination of #XmlID and #XmlIDREF. I will demonstrate below with an example.
JAVA MODEL
Graph
package forum13404583;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
class Graph {
#XmlElement(name = "vertex")
List<Vertex> vertexList;
#XmlElement(name = "edge")
List<Edge> edgeList;
}
Vertex
In the Vertex class you need to use the #XmlID annotation to indicate that the id field is the id.
package forum13404583;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
class Vertex {
#XmlAttribute
#XmlID
String id;
#XmlAttribute
String color;
#XmlAttribute
Integer thickness;
}
Edge
In the Edge class the #XmlIDREF annotation is used to indicate that the XML value contains a foreign key that references the real value.
package forum13404583;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
class Edge {
#XmlAttribute
#XmlIDREF
Vertex end1;
#XmlAttribute
#XmlIDREF
Vertex end2;
}
DEMO CODE
package forum13404583;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Graph.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum13404583/input.xml");
Graph graph = (Graph) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(graph, System.out);
}
}
INPUT (input.xml)/OUTPUT
Below is the input to and output from running the demo code.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<graph>
<vertex id="n1" color="red" thickness="2"/>
<vertex id="n2"/>
<edge end1="n1" end2="n2"/>
</graph>
For More Information
http://blog.bdoughan.com/2010/10/jaxb-and-shared-references-xmlid-and.html

Update partial XML mapping to bean

I need to map an xml file subset of nodes to a Java Bean.
For example map
<data>
<field1>Value</field1>
<field2>Value</field2>
<field3>Value</field3>
<field4>Value</field4>
<field5>Value</field5>
</data>
to
public class DataBean {
private String field2;
private String field5;
// ...getter/setter
}
then manipulate the bean and update the source xml file without loosing elements that are not mapped.
How can I use to do it?
What library?
Thanks for help,
Maurizio
Note: I'm the EclipseLink JAXB (MOXy) lead an a member of the JAXB 2 (JSR-222) expert group.
Below is how this can be done with MOXy's implementation of the JAXB Binder:
DataBean
package forum9988170;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="data")
public class DataBean {
private String field2;
private String field5;
public String getField2() {
return field2;
}
public void setField2(String field2) {
this.field2 = field2;
}
public String getField5() {
return field5;
}
public void setField5(String field5) {
this.field5 = field5;
}
}
jaxb.properties
To specify MOXy as your JAXB provider you need to add a file named jaxb.properties in the same package as your domain classes with the following entry,
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
package forum9988170;
import java.io.File;
import javax.xml.bind.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(DataBean.class);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
File xml = new File("src/forum9988170/input.xml");
Document document = db.parse(xml);
Binder<Node> binder = jc.createBinder();
DataBean dataBean = (DataBean) binder.unmarshal(document);
dataBean.setField2("NEW FIELD 2");
dataBean.setField5("NEW FIELD 5");
binder.updateXML(dataBean);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(System.out);
t.transform(source, result);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<data>
<field1>Value</field1>
<field2>NEW FIELD 2</field2>
<field3>Value</field3>
<field4>Value</field4>
<field5>NEW FIELD 5</field5>
</data>
For More Information
http://blog.bdoughan.com/2010/09/jaxb-xml-infoset-preservation.html
http://blog.bdoughan.com/2012/01/how-does-jaxb-compare-to-xmlbeans.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
If you decide what is in structure xml then you may use XStream (http://x-stream.github.io/) to serialize and deserialize.
But if you only deserialize from xml to bean (from foreign format), then you should use Smooks (http://www.smooks.org/).
Both of these libraries are very light in contrast to JAXB. JAXB is not flexible and requires that creating XML Schema. I do not recommend, because you lose more time than on creating a simple DOM parse.
JAXB is very academic. Example: many of "SOAP envelopes" is not fully described by WSDL documents, but adds some xml into WSDL field (in a simple text field). In such a case, you lose a lot of time to create a JAXB infrastructure...
Of course this is just my personal opinion. But remember these two tools and try to use them. You'll see that it really worth.

Convert Java object to XML

I am trying to convert a Java object inside of a Java library to an XML file. However, I got this problem:
A a = new A();
// initializing for a
JAXBContext jc = JAXBContext.newInstance("libraryA.A");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(a, System.out);
Then I got this exception:
javax.xml.bind.JAXBException: "libraryA.a" doesnt contain ObjectFactory.class or jaxb.index
at com.sun.xml.internal.bind.v2.ContextFactory.createContext(ContextFactory.java:186)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:128)
at javax.xml.bind.ContextFinder.find(ContextFinder.java:290)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:372)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:337)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:244)
If I change: JAXBContext jc = JAXBContext.newInstance("libraryA.a");
to:
JAXBContext jc = JAXBContext.newInstance(libraryA.A.class);
then I have another exception:
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions
library.A is an interface, and JAXB can't handle interfaces.
this problem is related to the following location:
at library.A
library.A does not have a no-arg default constructor.
this problem is related
to the following location:
at library.A
Background Info (From Related Question)
From the comment you made on my answer to your previous question the domain model is already being used with JAXB. The easiest way to have your client and server communicate via XML is to leverage the already annotated model on both ends.
I just have checked the source code of
my client. In the process, we need to
convert back a xml file which is
generated from java objects to xml
file using: javax.xml.bind.JAXBContext
& javax.xml.bind.Marshaller. so my
question is it possible to read back
the xml file to the same java objects?
then we can use the java objects for a
further step. Thanks in advance!
UPDATE
It appears as though your issue is due to having a domain model that is defined through interfaces with backing implementation classes. Below I'll demonstrate how you can handle this using a JAXB implementation (Metro, MOXy, JaxMe, etc).
Demo Code
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(CustomerImpl.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("input.xml");
Customer customer = (Customer) unmarshaller.unmarshal(xml);
Address address = customer.getAddress();
System.out.println(address.getStreet());
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(customer, System.out);
}
}
Interface Model
The following interfaces represent our domain model. These interfaces when not be leveraged to bootstrap the JAXBContext.
Customer
public interface Customer {
public Address getAddress();
public void setAddress(Address address);
}
Address
public interface Address {
public String getStreet();
public void setStreet(String street);
}
Implementation Classes
The implementation classes are what will be mapped to XML using JAXB.
CustomerImpl
Note in the CustomerImpl class we use the #XmlElement annotation on the address property to specify the type is AddressImpl.
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="customer")
public class CustomerImpl implements Customer {
private Address address;
#XmlElement(type=AddressImpl.class)
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
AddressImpl
public class AddressImpl implements Address {
private String street;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<customer>
<address>
<street>1 Any Street</street>
</address>
</customer>
If you don't have to use JAXB, perhaps you can use XStream ?
It imposes very few restrictions on what you can serialise to/from XML, and may be appropriate if you don't need JAXB specifically.

JAXB Adding attributes to a XmlElement for simple data types

I want to add some attributes to Xml Elements using JAXB when marshalling from JavaBeans. The Xml Elements are simple data types like String. So I do not want to create new Classes. For example, a desired output would be:
<notifications>
<date>04/20/2011</date>
<subject creditcard_num="22678" checknum="8904">Credit Card Charge Back</subject>
<body payment_amount="34.00" return_status="charged back">some text</body>
</notifications
I do not want to define subject and body as separate classes.
-Anand
My solution require defining a class for subject and body, but the desired output will be as requested
I use #XmlValue for the message and #XmlAttribute for the attributes
#Test
public void testAll() throws JAXBException
{
String msg = "<notifications><date>04/20/2011</date><subject creditcard_num='22678' checknum='8904'>Credit Card Charge Back</subject><body payment_amount='34.00' return_status='charged back'>some text</body></notifications>";
Notifications tested = (Notifications) JAXBContext.newInstance(Notifications.class).createUnmarshaller().unmarshal(new StringReader(msg));
assertEquals("Credit Card Charge Back",tested.subject.value);
assertEquals("8904",tested.subject.checknum);
assertEquals("22678",tested.subject.creditcard_num);
}
#XmlRootElement
public static class Notifications{
public String date;
public Subject subject;
}
public static class Subject
{
#XmlValue
public String value;
#XmlAttribute(name="creditcard_num")
public String creditcard_num;
#XmlAttribute(name="checknum")
public String checknum;
}
NOTE:I only wrote the subject part, I wonder if using #XmlPath can be used to remove the need for different classes
You could use EclipseLink JAXB (MOXy)'s #XmlPath annotation to solve this problem (I'm the MOXy tech lead):
Notifications
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Notifications {
private String date;
#XmlPath("subject/#creditcard_num")
private String creditcardNum;
#XmlPath("subject/#checknum")
private String checknum;
private String subject;
#XmlPath("body/#payment_amount")
private String paymentAmount;
#XmlPath("body/#return_status")
private String returnStatus;
private String body;
}
jaxb.properties
To use MOXy as your JAXB implementation you need to put a file named jaxb.properties in the same package as your model classes with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Notifications.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Notifications notifications = (Notifications) unmarshaller.unmarshal(new File("input.xml"));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(notifications, System.out);
}
}
For More Information:
http://bdoughan.blogspot.com/2010/07/xpath-based-mapping.html
http://bdoughan.blogspot.com/2010/09/xpath-based-mapping-geocode-example.html
http://bdoughan.blogspot.com/2011/03/map-to-element-based-on-attribute-value.html

Categories