I'm trying to unmarshall the following xml:
<?xml version="1.0" encoding="UTF-8" ?>
<container>
<childs>
<child uuid="1" childB="3" attrA="specialAttrA1" />
<child uuid="2" childB="4" attrA="specialAttrA2" />
<child uuid="3" attrB="specialAttrB1" />
<child uuid="4" attrB="specialAttrB2" />
</childs>
</container>
As far as I can see this scenario contains two major problems:
modelling the object hierachy
referencing a subclass from within another subclass
The object hierachy consists of ChildA and ChildB which are subclasses of Parent (See the sourcecode below).
In the end the unmarshalled objects look good besides the references 'childB' which are null.
I couldn't find any example for this scenario. Debugging the source below questions me why the FlatParent object passed to the unmarshal-method of the Adapter doesn't contain childB.
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Container {
#XmlElementWrapper(name = "childs")
#XmlElement(name = "child")
private List<Parent> childs = new ArrayList<>();
}
#XmlRootElement
#XmlAccessorType(value = XmlAccessType.FIELD)
#XmlJavaTypeAdapter(value = ParentAdapter.class, type = Parent.class)
public abstract class Parent {
#XmlID
#XmlAttribute
private String uuid;
}
#XmlRootElement
#XmlAccessorType(value = XmlAccessType.FIELD)
public class ChildA extends Parent{
#XmlAttribute
private String attrA;
#XmlIDREF
#XmlAttribute
private ChildB childB;
}
#XmlRootElement
#XmlAccessorType(value = XmlAccessType.FIELD)
public class ChildB extends Parent{
#XmlAttribute
private String attrB;
}
The corresponding Adapter looks like:
public class ParentAdapter extends XmlAdapter<FlatParent, Parent> {
public Parent unmarshal(FlatParent fp) {
Parent p = null;
if (fp != null){
if (fp.getAttrA() != null){
p = new ChildA();
((ChildA) p).setAttrA(fp.getAttrA());
((ChildA) p).setChildB(fp.getChildB());
} else {
p = new ChildB();
((ChildB) p).setAttrB(fp.getAttrB());
}
p.setUuid(fp.getUuid());
}
return null;
}
...
}
And the containing FlatParent:
#XmlRootElement
#XmlAccessorType(value = XmlAccessType.FIELD)
public class FlatParent {
#XmlID
#XmlAttribute
private String uuid;
#XmlAttribute
private String attrA;
#XmlAttribute
private String attrB;
#XmlIDREF
#XmlElement
private ChildB childB;
}
Just to be completely here is the part of the Testing class also:
#Test
public void testImportXml() throws JAXBException {
JAXBContext jc = JAXBContext.newInstance(Container.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Container cont = (Container) unmarshaller.unmarshal(new File(FILEPATH));
You have two option I think:
1, Use an unmarshaller-listener, the drawback is that you have to read the XML twice and update your references manually after every involved object were unmarshalled
2, Read your XML as is, with numbers and convert them after unmarshalling to another pojos using a java bean mapper, e.g. MapStruct (In mapstruct, the fields with the same name mapped automatically and you can do this reference mapping in a decorator subclass)
Related
I would like to ask on how to marshal an object with property order from a nested object.
#XmlRootElement(name = "sample")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(propOrder = {"title", "code"})
public class SampleObject {
#XmlAttribute(name = "title")
private String title;
#XmlAttribute(name = "code")
private String code;
}
I have a wrapperList to set that object:
#XmlRootElement(name = "listWrapper")
#XmlAccessorType(XmlAccessType.FIELD)
public class WrapperObject {
#XmlAnyElement(lax=true)
private List<SampleObject> objectList;
}
And i want to set the list on this object. This object is the one who is being marshalled.
#XmlRootElement(name = "marshaller")
#XmlAccessorType(XmlAccessType.FIELD)
public class MarshallerObject {
#XmlElement(name = "wrapperList")
private WrapperObject objectList;
}
This is the output that i'm aiming for:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<marshaller>
<wrapperList>
<sample title="sampleTitle code"001"/>
</wrapperList>
</marshaller>
</soap:Envelope>
Thanks in advance!
I am trying to marshall the inputs using JAXB annotations and I am facing some small issues. Wanted to confirm if it's possible to achieve
I have two classes parent and child each of which has many fields. child class extends parent class.
Is it possible to add the elements from parent class in the child class prepOrder for #XmlType?
Is it necessary to add all elements within the propOrder? for example if I have 10 fields out of which I want only 4 fields to be ordered. Rest can appear in any order they want. Is it possible to do this? Because when I do not add a field then I am getting the error.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Parent",propOrder={"city","year"})
public class Parent{
private String brand;
private String city;
private String year;
//Getters and Setters avoided
}
#XmlRootElement(name = "Car")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Child",propOrder={"engine","brand","build"})
public class Child{
private String engine;
private String build;
//Getter and Setters avoided
}
public class Main{
public static void main(String []args){
Child child = new Child();
//Adding values and creating xml using the Marshalling approach
JAXBContext context = JAXBContext.newInstance(Child.class);
Marshaller mar = context.createMarshaller();
mar.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
mar.marshal(child, System.out);
}
}
The final xml that I am looking forward to is:
<?xml version="1.0"?>
<child>
<cit>Frankfurt</cit>
<year>2021</year>
<engine>Mercedes</engine>
<brand>Ferari</brand>
<build>Germany</build>
</child>
After a lot of trial and error methods, I was able to do it. Posting the answer as it can be useful to someone else in the future:
Add the #XmlTransient annotation on the Parent class and remove the propOrder.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Parent")
#XmlTransient
public class Parent{
//All the parent element fields and other codes getter/setters
}
Add all the fields in Child class which is extending Parent
#XmlRootElement(name = "Car")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Child",propOrder={"city","year","engine","brand","build"})
public class Child{
}
I'm stucked at parsing the following xml with JAXB:
<?xml version="1.0" encoding="utf-8"?>
<dashboardreport name="exampleDashboard" version="6.5.6.1013" reportdate="2016-12-16T11:05:19.329+01:00" description="">
<data>
<incidentchartdashlet name="Incident Chart" description="" />
<chartdashlet name="WebRequestTime" showabsolutevalues="false" />
<chartdashlet name="WebServiceTime" showabsolutevalues="false" />
</data>
</dashboardreport>
I used the following java classes to unmarshal the xml:
Dashboardreport.java
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "dashboardreport")
public class Dashboardreport {
#XmlElementWrapper(name = "data")
#XmlElement(name = "chartdashlet")
protected List<Chartdashlet> chartdashlets;
#XmlElementWrapper(name = "data")
#XmlElement(name = "incidentchartdashlet")
protected List<Incidentchartdashlet> incidentchartdashlets;
#XmlAttribute(name = "name")
protected String name;
}
I just want to unmarshal the xml without using a wrapper class around incidentchartdashlets and chartdashlet, cause both types differ a lot.
I only can use the XmlElementWrapper annotation once, so that only chartdashlets get filled and incidentchartdashlets is null.
Is there any solution with JAXB without using a seperate wrapper class?
I assume your dashlet classes are defined like
class Chartdashlet extends Dashlet and class Incidentchartdashlet extends Dashlet.
Then the preferred JAXB way to handle your mixed list of dashlets would be
by using the #XmlElements
annotation:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "dashboardreport")
public class Dashboardreport {
#XmlElementWrapper(name = "data")
#XmlElements({
#XmlElement(name = "chartdashlet", type = Chartdashlet.class),
#XmlElement(name = "incidentchartdashlet", type = Incidentchartdashlet.class)
})
protected List<Dashlet> dashlets;
#XmlAttribute(name = "name")
protected String name;
}
i am trying to use one class to map the response i get from an XML request.
But the xml response differs, depending own some settings. For example in a response i get the tag "owner" which is filled with the ID of the owner object. If i add a setting in my request i will get back the full owner data, like the firstname and lastname. Now i want to map the owner tag to either a String variable or a Class depending on the response.
Example :
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "domain")
public class Response {
#XmlElement
private String name;
#XmlElement(name = "owner")
private String ownerSimple;
#XmlElement(name = "owner")
private Owner ownerComplex;
}
#XmlRootElement(name = "ownerc")
public class OwnerC {
#XmlElement
int id;
#XmlElement
String fname;
#XmlElement
String lname;
}
XML to map :
<response>
<name>Foo</name>
<owner>1234</owner> <!-- in this case it's only a id -->
</response>
<response>
<name>Foo</name>
<owner> <!-- in this case it's the owner class -->
<id>1234</id>
<fname>Jon</fname>
<lname>Doe</lname>
</owner>
</response>
You can use #XmlAnyElement(lax=true) to handle this use case. This annotation allows you to unmarshall any XML to a Java object (DOM Node). In a second step, it is possible to unmarshall the Node to the required Object
Response
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "domain")
public class Response {
#XmlElement
private String name;
#XmlAnyElement(lax=true)
private Object owner;
private String ownerSimple;
#XmlTransient
private Owner ownerComplex;
Owner
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "owner")
public class Owner {
#XmlElement
int id;
#XmlElement
String fname;
#XmlElement
String lname;
Unmarshaller
//Unmarshaller. Step 1 - Decodes Response and set a DOM Node at Owner
//Important. Owner class must not be present in JAXB context, letting next step to decode the object properly.
//Owner variable at Response class is annotated with #XmlTransient
JAXBContext jaxbContext = JAXBContext.newInstance(Response.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Response response = (Response) jaxbUnmarshaller.unmarshal(reader);
//Unmarshaller. Step 2. Convert Node to the suitable Object
//Considering both cases, simple-> String complex -> Owner Object.
String ownerSimple = ((Node)response.getOwner()).getFirstChild().getNodeValue();
if (ownerSimple != null){
response.setOwnerSimple(ownerSimple);
} else {
JAXBContext jaxbContextOwner = JAXBContext.newInstance(Owner.class);
Unmarshaller jaxbUnmarshallerOwner = jaxbContextOwner.createUnmarshaller();
Owner ownerComplex = (Owner) jaxbUnmarshallerOwner.unmarshal((Node)response.getOwner());
response.setOwnerComplex(ownerComplex);
}
//Marshaller to system.out. Your object is well mapped in both cases
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(rx, System.out);
I am trying to use JAXB and give the response in XML but I cannot access the members from Parent class. I am getting the following response without the lastname field which is in the parent class.
<A>
<Child>
<firstname>ABC</firstName>
</Child>
</A>
I have a class in this sample format:
#xmlrootelement
class A{
#xmlelement(required = true)
List<Child> childList;
}
class Parent{
#xmlelement(required = true)
protected string lastName;
}
#XmlRootelement
class Child extends Parent{
#xmlelement(required = true)
String firstName;
}