I am having issues to unmarshall nested xml below. Can someone please advise if I am missing something.
body tag can contain any Jaxb anotated obj.
Do I have to create a custom adapter for marshalling/unmarshalling such xml?
Input XML
<?xml version="1.0" encoding="UTF-8"?>
<serviceRq xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="serviceRq">
<body>
<createRq>
<id>1234</id>
</createRq>
</body>
</serviceRq>
My Jaxb-annotated classes are:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "serviceRq")
public class ServiceRq{
private Object body;
<!-- getters and setters omitted-->
}
Here, body can be any jaxb annotated object, in this case its CreateRq.
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "createRq")
public class CreateRq{
private String id;
<!-- getters and setters omitted-->
}
I am looking for a generic way to support any Jaxb annotated object in body of the input xml.
You could use a #XmlAnyElement(lax=true) and an XmlAdapter to handle this use case:
ServiceRq
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "serviceRq")
public class ServiceRq{
#XmlJavaTypeAdapter(value=BodyAdapter.class)
private Object body;
// getters and setters omitted
}
BodyAdapter
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class BodyAdapter extends XmlAdapter<Body, Object>{
#Override
public Object unmarshal(Body v) throws Exception {
return v.getValue();
}
#Override
public Body marshal(Object v) throws Exception {
Body body = new Body();
body.setValue(v);
return body;
}
}
Body
import javax.xml.bind.annotation.XmlAnyElement;
public class Body {
private Object value;
#XmlAnyElement(lax=true)
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
CreateRq
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "createRq")
public class CreateRq{
private String id;
// getters and setters omitted
}
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(ServiceRq.class);
System.out.println(jc);
Unmarshaller unmarshaller = jc.createUnmarshaller();
ServiceRq serviceRq = (ServiceRq) unmarshaller.unmarshal(new File("input.xml"));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(serviceRq, System.out);
}
}
For More Information
http://bdoughan.blogspot.com/2010/08/using-xmlanyelement-to-build-generic.html
http://bdoughan.blogspot.com/2010/07/xmladapter-jaxbs-secret-weapon.html
http://bdoughan.blogspot.com/2010/12/jaxb-and-immutable-objects.html
http://bdoughan.blogspot.com/2010/12/represent-string-values-as-element.html
You could use #XmlAnyElement(lax=true) and the #XmlPath extension in EclipseLink JAXB (MOXy) to handle this use case (Note: I'm the MOXy lead). For an approach that would work with any JAXB implementation (Metro, MOXy, JaxMe, etc) see: Jaxb complex xml unmarshall.
ServiceRq
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "serviceRq")
public class ServiceRq{
#XmlPath("body/createRq")
#XmlAnyElement(lax=true)
private Object body;
// getters and setters omitted
}
CreateRq
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "createRq")
public class CreateRq{
private String id;
// getters and setters omitted
}
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(ServiceRq.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
ServiceRq serviceRq = (ServiceRq) unmarshaller.unmarshal(new File("input.xml"));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(serviceRq, System.out);
}
}
jaxb.properties
To use MOXy as your JAXB provider you must include a file named jaxb.properties in the same package as your domain model with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
For More Information
http://bdoughan.blogspot.com/2010/08/using-xmlanyelement-to-build-generic.html
http://bdoughan.blogspot.com/2011/05/specifying-eclipselink-moxy-as-your.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
Related
I want to store my object data to XML. Below code spinets will show the example of model class.
Class Model
{
#XmlElement
private int id;
#XmlElement
Private string name;
}
I will have multiple model objects which will be stored in some list as below
#XmlRootElement
Class ModelWrapper
{
#XmlElement
#XmlJavaTypeAdapter(value = ListAdapter.class, type = List.class)
List<model> list;
public setlist(List<model>list)
{
//setting list
}
public List<model> getlist()
{
return list
}
}
Now if I marshall this using JAXB, something like this will be produced:
<Modelwrapper>
<List>
......
......
</List>
</Modelwrapper>
I want to avoid one of the root may be list or Modelwrapper.
Is there any way to do it?
In this way your xml will be
<ModelWrapper>
<model>
......
</model>
<model>
......
</model>
</ModelWrapper>
ModelWrapper.java
#XmlRootElement
public class ModelWrapper {
#XmlElement(name = "model")
protected List<Model> list;
Test
ModelWrapper.java
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "ModelWrapper", propOrder = {
"list"
})
public class ModelWrapper {
#XmlElement(name = "model")
private List<Model> list;
public List<Model> getList() {
return list;
}
public void setList(List<Model> list) {
this.list = list;
}
}
Model.java
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Model", propOrder = {
"id","name"
})
public class Model {
#XmlElement
private int id;
#XmlElement
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Main.java
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
public class Main {
public static void main(String[] args) throws JAXBException {
JAXBContext jc = JAXBContext.newInstance(ModelWrapper.class);
ModelWrapper mw = new ModelWrapper();
List<Model> list = new ArrayList<Model>();
Model m = new Model();
m.setId(1);
m.setName("model1");
list.add(m);
m = new Model();
m.setId(1);
m.setName("model2");
list.add(m);
mw.setList(list);
Marshaller mar = jc.createMarshaller();
mar.marshal(mw, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<modelWrapper>
<model>
<id>1</id>
<name>model1</name>
</model>
<model>
<id>1</id>
<name>model2</name>
</model>
</modelWrapper>
I'm trying to parse an xml file using JAXB.
My problem is that I need to skip the root node, If I delete it from the xml file I get what I need, otherwise - I get an empty object.
I'll give a simplified xml and my code (It behaves the same way):
XML:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<!-- <Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="Office.xsd">
-->
<Office>
<Employees>
<Employee>
<name>George</name>
<rank>3</rank>
</Employee>
<Employee>
<name>Michael</name>
<rank>5</rank>
</Employee>
<Employee>
<name>Jeff</name>
<rank>1</rank>
</Employee>
<Employee>
<name>Todd</name>
<rank>7</rank>
</Employee>
<Employee>
<name>Jessica</name>
<rank>5</rank>
</Employee>
</Employees>
</Office>
</Root>
Office class:
import javax.xml.bind.annotation.*;
import java.util.Vector;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Office {
#XmlElementWrapper(name = "Employees")
#XmlElement(name = "Employee")
private Vector<Employee> employees;
}
Employee class:
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
#XmlAccessorType(XmlAccessType.FIELD)
public class Employee {
#XmlElement(name="name")
private String name;
#XmlElement(name="rank")
private int rank;
public void promote() {
rank++;
}
}
Driver:
import javax.xml.stream.*;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.Marshaller;
import java.io.FileReader;
public class Driver {
public static void main (String[] args) {
parseOffice();
}
public static void parseOffice() {
try {
XMLInputFactory f = XMLInputFactory.newInstance();
XMLStreamReader reader = f.createXMLStreamReader(new FileReader("Office.xml"));
JAXBContext context = JAXBContext.newInstance(Office.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
Office office = unmarshaller.unmarshal(reader, Office.class).getValue();
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(office, System.out);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
You can parse the XML with a StAX XMLStreamReader, then advance it to the element you want to unmarshal, and then unmarshal it.
I posted a full example that should help on the related question linked below:
How to unmarshall SOAP response using JAXB if namespace declaration is on SOAP envelope?
Why don't you create a generic root element?
#XmlRootElement(name="Root" ...)
public class Root {
#XmlAnyElement(lax=true)
private Object content;
}
Add it to your context and unmarshal. You should get a JAXBElement<Office> as content.
Simply add the root class in hierarcy. And get Office class from Root class.
Root Class:-
import javax.xml.bind.annotation.*;
import java.util.Vector;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlElement(name = "Office")
private Office office;
}
Office class
import javax.xml.bind.annotation.*;
import java.util.Vector;
#XmlAccessorType(XmlAccessType.FIELD)
public class Office {
#XmlElementWrapper(name = "Employees")
#XmlElement(name = "Employee")
private Vector<Employee> employees;
}
Change in parse method :-
JAXBContext context = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
Root root = unmarshaller.unmarshal(reader, Root.class).getValue();
Office office = root.getOffice();
This is my xml, need to convert it into java. I had used jaxb
<?xml version="1.0"?>
<lm:order Id="PLG24M240U" JD="" aCount="2" SUCount="1" xmlns:lm="http://www.ae.com/Event/Load">
<lm:master>
<lm:ID>3</lm:ID>
<lm:Number>313</lm:Number>
<lm:ANumber>323</lm:ANumber>
</lm:master>
<lm:detail>
<lm:ID>3</lm:ID>
<lm:Number>3131</lm:Number>
<lm:ANumber>3232</lm:ANumber>
</lm:detail>
<lm:detail>
<lm:ID>3</lm:ID>
<lm:Number>3131</lm:Number>
<lm:ANumber>3232</lm:ANumber>
</lm:detail>
<lm:detail>
<lm:ID>3</lm:ID>
<lm:Number>313</lm:Number>
<lm:ANumber>323</lm:ANumber>
</lm:detail>
</lm:order>
And throwing the following exception
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.ae.com/Event/Load", local:"Order"). Expected elements are <{}lm:Order>
This is my unmarshalling code
jaxbContext = JAXBContext.newInstance(Order.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Order order = (Order) jaxbUnmarshaller.unmarshal(file);
System.out.println(order );
Order Pojo class
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "lm:Order")
public class OrderPay {
#XmlAttribute
private String Id;
#XmlAttribute
private String JD;
#XmlAttribute
private String aCount;
#XmlAttribute
private String pCount;
/*#XmlElement
private Master master;
#XmlElement
private List<Detail> details = new ArrayList<Detail>();*/
}
Can you please help me in reading also, currently reading through file, need to read as an XML String.
The namespace attribute xmlns:lm="http://www.ae.com/Event/Load" might be the culprit here. In order to specify the namespace prefix, you can add the #XmlSchema annotation to a package-info.java file like this:
#XmlSchema(
namespace="http://www.ae.com/Event/Load",
elementFormDefault=XmlNsForm.QUALIFIED),
xmlns={#XmlNs(prefix="lm", namespaceURI="http://www.ae.com/Event/Load")})
package your.package;
import javax.xml.bind.annotation.*;
I have a JAXB-annotated POJO like this:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Clazz implements Serializable {
#XmlElement(required = false)
private int a;
#XmlElement(required = false)
private int b;
}
I want to mark that either field a or field b is required. With my current set-up, none of them are required, but I want one of them to be present and not the other. How could I achieve it?
You can do the following with #XmlElementRefs
Domain Model
Clazz
import java.io.Serializable;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Clazz implements Serializable {
#XmlElementRefs({
#XmlElementRef(name="a", type=JAXBElement.class),
#XmlElementRef(name="b", type=JAXBElement.class)
})
private JAXBElement<Integer> aOrB;
}
ObjectFactory
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
#XmlElementDecl(name = "a")
public JAXBElement<Integer> createA(Integer integer) {
return new JAXBElement<Integer>(new QName("a"), Integer.class, integer);
}
#XmlElementDecl(name = "b")
public JAXBElement<Integer> createB(Integer integer) {
return new JAXBElement<Integer>(new QName("b"), Integer.class, integer);
}
}
Demo Code
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Clazz.class, ObjectFactory.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum22502171/input.xml");
Clazz clazz = (Clazz) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(clazz, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8"?>
<clazz>
<b>123</b>
</clazz>
I'm switching from Castor to JAXB2 to perform marshaling/unmarshaling between XML and Java object. I'm having problem trying to configure a collection of polymorphic objects.
Sample XML
<project name="test project">
<orange name="fruit orange" orangeKey="100" />
<apple name="fruit apple" appleKey="200" />
<orange name="fruit orange again" orangeKey="500" />
</project>
Project class
The oranges list works fine, I'm seeing 2 oranges in the list. But, I'm not sure how to configure fruitList. The fruitList should have 3 fruit: 2 oranges and 1 apple.
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Project {
#XmlAttribute
private String name;
#XmlElement(name = "orange")
private List<Orange> oranges = new ArrayList<Orange>();
// Not sure how to configure this... help!
private List<Fruit> fruitList = new ArrayList<Fruit>();
}
Fruit class
Fruit is an abstract class. For some reason, defining this class as an abstract seems to be causing a lot of problems.
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public abstract class Fruit {
#XmlAttribute
private String name;
}
Orange class
public class Orange extends Fruit {
#XmlAttribute
private String orangeKey;
}
Apple class
public class Apple extends Fruit {
#XmlAttribute
private String appleKey;
}
How do I configure my fruitList in Project to achieve what I want here?
Thanks much!
You want to leverage #XmlElementRef this is corresponds to the XML schema concept of substitution groups which corresponds to your question.
Step 1 - Using #XmlElementRef
The fruitList property is annotated with #XmlElementRef:
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Project {
#XmlAttribute
private String name;
#XmlElementRef
private List<Fruit> fruitList = new ArrayList<Fruit>();
}
Step 2 - Annotate Apple and Orange with #XmlRootElement
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Apple extends Fruit {
#XmlAttribute
private String appleKey;
}
and
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Orange extends Fruit {
#XmlAttribute
private String orangeKey;
}
Demo Code
The following code can be used to demonstrate the solution:
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(Project.class, Apple.class, Orange.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Project project = (Project) unmarshaller.unmarshal(new File("input.xml"));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(project, System.out);
}
}
For More Information:
http://bdoughan.blogspot.com/2010/11/jaxb-and-inheritance-using-substitution.html
After futzing around... I think I got it working now:-
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Project {
#XmlAttribute
private String name;
// this has to be commented out, or else oranges won't show up in fruitList
// #XmlElement(name = "orange")
// private List<Orange> oranges = new ArrayList<Orange>();
#XmlElements({
#XmlElement(name = "orange", type = Orange.class),
#XmlElement(name = "apple", type = Apple.class)
})
private List<Fruit> fruitList = new ArrayList<Fruit>();
}
Put XmlAnyElement annotation:
#XmlAnyElement(lax = true)
private List<Fruit> fruitList = new ArrayList<Fruit>();