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
Related
I'm new to using namespaces in xml so I am kind of confused and would like some clarification. I have a java service where I am receiving xml documents with many different namespaces and while i got it working, I feel like I must have done something wrong so I want to check. In my package-info.java I have my schema annotation such as:
#javax.xml.bind.annotation.XmlSchema(
xmlns={
#javax.xml.bind.annotation.XmHS(prefix="train", namespaceURI="http://mycompany/train"),
#javax.xml.bind.annotation.XmHS(prefix="passenger", namespaceURI="http://mycompany/passenger")
},
elementFormDefault = javax.xml.bind.annotation.XmlNsForm=QUALIFIED
)
I have a Train.java annotated on the class level with:
#XmlRootElement(name="Train", namespace="http://mycompany/train")
and each field in the class annotated with:
#XmlElement(name="Color") for example
Train contains a List of Passenger(s) so there's a property
private Set<Passenger> passengers;
and this collection is annotated with:
#XmlElementWrapper(name="Passengers")
#XmlElements(#XmlElement(name="Passenger", namespace="http://mycompany/passenger"))
Then within Passenger.java the class itself is annotated with:
#XmlElement(name="Passenger", namespace="http://mycompany/passenger")
Finally for individual fields within Passenger.java, they are annotated like this:
#XmlElement(name="TicketNumber", namespace="http://mycompany/passenger")
So when I have an xml that looks like:
<train:Train>
<train:Color>Red</train:Color>
<train:Passengers>
<train:Passenger>
<passenger:TicketNumber>T101</passenger:TicketNumber>
</train:Passenger>
</train:Passengers>
</train:Train>
Now I unmarshal this xml I received and Train's Color property is set and Passenger's TicketNumber property is set. But I don't know why I need to add the namespace url on the XmlElement annotation on TicketNumber for that to work but I didn't need to do so for the Color property on Train. If I remove the namespace attribute from the XmlElement annotation on TicketNumber, the value from the xml wont get mapped to the object unless I also remove the namespace prefix from the xml request. I feel like since I've got the namespace attribute defined on the XmlRootElement for Passenger, I shouldn't need to do that for every single field in the class as well just like I didn't have to for Train so I am assuming I must have setup something wrong. Can someone point me in the right direction? Thanks!
Below is an explanation of how namespaces work in JAXB (JSR-222) based on your model.
JAVA MODEL
package-info
Below is a modified version of your #XmlSchema annotation. It contains some key information:
namespace - The default namespace that will be used to qualify global elements (those corresponding to #XmlRootElement and #XmlElementDecl annotations (and local elements based on the elementFormDefault value) that don't have another namespace specified.
elementFormDefault by default only global elements are namespace qualified but by setting the value to be XmlNsForm.QUALIFIED all elements without an explicit namespace specified will be qualified with the namespace value.
xmlns is the preferred set of prefixes that a JAXB impl should use for those namespaces (although they may use other prefixes).
#XmlSchema(
namespace="http://mycompany/train",
elementFormDefault = XmlNsForm.QUALIFIED,
xmlns={
#XmlNs(prefix="train", namespaceURI="http://mycompany/train"),
#XmlNs(prefix="passenger", namespaceURI="http://mycompany/passenger")
}
)
package forum15772478;
import javax.xml.bind.annotation.*;
Train
Since all the elements corresponding to the Train class correspond to the namespace specified on the #XmlSchema annotation, we don't need to specify any namespace info.
Global Elements - The #XmlRootElement annotation corresponds to a global element.
Local Elements - The #XmlElementWrapper and #XmlElement annotations correspond to local elements.
package forum15772478;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="Train")
public class Train {
private List<Passenger> passengers;
#XmlElementWrapper(name="Passengers")
#XmlElement(name="Passenger")
public List<Passenger> getPassengers() {
return passengers;
}
public void setPassengers(List<Passenger> passengers) {
this.passengers = passengers;
}
}
Passenger
If all the elements corresponding to properties on the Passenger class will be in the http://mycompany/passenger namespace, then you can use the #XmlType annotation to override the namespace from the #XmlSchema annotation.
package forum15772478;
import javax.xml.bind.annotation.*;
#XmlType(namespace="http://mycompany/passenger")
public class Passenger {
private String ticketNumber;
#XmlElement(name="TicketNumber")
public String getTicketNumber() {
return ticketNumber;
}
public void setTicketNumber(String ticketNumber) {
this.ticketNumber = ticketNumber;
}
}
Alternatively you can override the namespace at the property level.
package forum15772478;
import javax.xml.bind.annotation.*;
public class Passenger {
private String ticketNumber;
#XmlElement(
namespace="http://mycompany/passenger",
name="TicketNumber")
public String getTicketNumber() {
return ticketNumber;
}
public void setTicketNumber(String ticketNumber) {
this.ticketNumber = ticketNumber;
}
}
DEMO CODE
The following demo code can be run to prove that everything works:
Demo
package forum15772478;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Train.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum15772478/input.xml");
Train train = (Train) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(train, System.out);
}
}
input.xml/Output
In the XML below I have added the necessary namespace declarations that were missing from the XML document in your question.
<train:Train
xmlns:train="http://mycompany/train"
xmlns:passenger="http://mycompany/passenger">
<train:Color>Red</train:Color>
<train:Passengers>
<train:Passenger>
<passenger:TicketNumber>T101</passenger:TicketNumber>
</train:Passenger>
</train:Passengers>
</train:Train>
FOR MORE INFORMATION
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
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.
Say I have two JavaBeans Person and Address.
If I create a list of Person objects, I'd like to marshal to something like this:
<persons>
<person>...</person>
</persons>
It's possible to use the technique described here:
Using JAXB to unmarshal/marshal a List<String>
By annotating JaxbList with #XmlRootElement(name = "persons") and #XmlElement(name = "person"), then it's possible to marshal to the XML above.
But, it'd be nice to be able to reuse the same JaxbList<T> class to also marshal a list of Address objects. And in reality, I will have many other types of beans. I can go with something like:
<list>
<item xsi:type="person" xmlns:xsi="http://www.w2.org/2001/XmlSchema-instance"></item>
</list>
But, ideally, it'd be nice to have it replace "list" with the plural version of class name and "item" with the class name.
So, is it possible to programmatically configure the JaxbContext or something during runtime and essentially set the value of the name inside #XmlRootElement and #XmlElement?
Or any other way to get this working without having to write a separate implementation of JaxbList for every bean type? Maybe XmlJavaTypeAdapter can achieve this sort of thing?
Update
#Blaise Doughan's solution accepted below works great. For my use case, I needed to go straight from Java object to XML, here's what worked (note this is not my full implementation, it's sort of just pseudo code for demonstration):
//JAXBContext is thread safe and so create it in constructor or
//setter or wherever:
...
JAXBContext jc = JAXBContext.newInstance(Wrapper.class, clazz);
...
public String marshal(List<T> things, Class clazz) {
//configure JAXB and marshaller
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
//Create wrapper based on generic list of objects
Wrapper<T> wrapper = new Wrapper<T>(things);
JAXBElement<Wrapper> wrapperJAXBElement = new JAXBElement<Wrapper>(new QName(clazz.getSimpleName().toLowerCase()+"s"), Wrapper.class, wrapper);
StringWriter result = new StringWriter();
//marshal!
m.marshal(wrapperJAXBElement, result);
return result.toString();
}
You could create a generic Wrapper object like the following:
Wrapper
You could create a generic wrapper class with a List property annotated with #XmlAnyElement(lax=true). The type of the object used to populate this list will be based on its root element (see: http://blog.bdoughan.com/2010/08/using-xmlanyelement-to-build-generic.html).
package forum13272288;
import java.util.*;
import javax.xml.bind.annotation.XmlAnyElement;
public class Wrapper<T> {
private List<T> items = new ArrayList<T>();
#XmlAnyElement(lax=true)
public List<T> getItems() {
return items;
}
}
Address
You will need to annotate the possible contents of the list with #XmlRootElement.
package forum13272288;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Address {
}
Person
package forum13272288;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Person {
}
Demo
The demo code below demonstrates how to use the Wrapper class. Since the root element can be different you will need to specify that you want to unmarshal to the wrapper class. Alternatively you could leverage the #XmlElementDecl annotation to associate multiple root elements with the wrapper class (see: http://blog.bdoughan.com/2012/07/jaxb-and-root-elements.html).
package forum13272288;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Wrapper.class, Person.class, Address.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StreamSource personsXML = new StreamSource("src/forum13272288/persons.xml");
JAXBElement<Wrapper> wrapper1 = unmarshaller.unmarshal(personsXML, Wrapper.class);
marshaller.marshal(wrapper1, System.out);
StreamSource addressesXML = new StreamSource("src/forum13272288/addresses.xml");
JAXBElement<Wrapper> wrapper2 = unmarshaller.unmarshal(addressesXML, Wrapper.class);
marshaller.marshal(wrapper2, System.out);
}
}
Output
Below is the output from running the demo code. The files persons.xml and addresses.xml look just like there corresponding output.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persons>
<person/>
<person/>
</persons>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addresses>
<address/>
<address/>
</addresses>
For More Information
http://blog.bdoughan.com/2012/11/creating-generic-list-wrapper-in-jaxb.html
I am working on a Scala project, and we want to use XML to initialize our objects with JAXB (not Spring). I have a hierarchy where more data members get added in the subclasses. A simple example would look something like this:
class Animal
{
string name
}
class Cat extends Animal
{
int numLives
}
class Dog extends Animal
{
bool hasSpots
}
I would like to be able to initialize a list of animals from an XML block that looks something like this:
<Animals>
<Cat>
<name>Garfield</name>
<numLives>9</numLives>
</Cat>
<Dog>
<name>Odie</name>
<hasSpots>false</hasSpots>
</Dog>
</Animals>
How would we setup the annotations in the classes to be able to handle this?
For this example you will want to make use of the #XmlElementRef and #XmlRootElement annotations. This corresponds to the XML schema concept of substitution groups. This will allow you to have a list of objects from an inheritance hierarchy differentiated by element.
Animals
This will serve as the root object for the domain model. It has a List property annotated with #XmlElementRef. This means it will match values based on the value of their #XmlRootElement annotations.
package forum8356849;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="Animals")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({Cat.class, Dog.class})
public class Animals {
#XmlElementRef
private List<Animal> animals;
}
Animal
package forum8356849;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
class Animal
{
String name;
}
Cat
We will annotate the Cat class with an #XmlRootElement annotation. This is used in tandem with the #XmlElementRef annotation on Animals.
package forum8356849;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="Cat")
class Cat extends Animal
{
int numLives;
}
Dog
We will also add an #XmlRootElement annotation to the Dog class.
package forum8356849;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="Dog")
class Dog extends Animal
{
boolean hasSpots;
}
Demo
You can use the following class to see that everything works as expected. input.xml corresponds to the XML provided in your question.
package forum8356849;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Animals.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum8356849/input.xml");
Animals animals = (Animals) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(animals, System.out);
}
}
For More Inforation
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-substitution.html
In such cases, I prefer creating an XSD schema and generating code from it, so you are on safe side.
But to answer your question, yes, you can. The annotations are XMLElement, XMLAttribute, XMLRootElement.
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.