I have a XML in which some common part that wrappes a specific part that can change in any way.
For example I would have to manage these 2 XML (simplified):
...
<xml>
<common>
<data1>1</data1>
<data2>2</data2>
</common>
<specific>
<specific-info>
<color>blue</color>
</specific-info>
</specific>
</xml>
...
And this one:
...
<xml>
<common>
<data1>33</data1>
<data2>42</data2>
</common>
<specific>
<another-info>
<age>42</age>
</another-info>
</specific>
</xml>
...
So I've inherit this code (simplified), using JAXB, that works:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {})
#XmlRootElement(name = "xml")
public class Specific{
#XmlElement(name = "common", required = true)
protected CommonData common;
#XmlElement(name = "specific")
protected SpecificInfo specificInfo;
#XmlElement(name = "specific")
protected AnotherInfo anotherInfo;
// getters and setters
}
The problem is that when a new sort of info arrives I've to add new XMLElement with same name and I think that it smells... And also that they are getters an setters for each of it.
There's another way to afford this? It's a standar way to unwrap a wrapped XML with JAXB?
In the way that #fisc showed me I used in my bean #XmlAnyElement in his way:
#XmlAnyElement(lax=true)
public Object[] others;
Which gets the specific part of the xml as an xml DOM object and ALSO this method to get the actual object and not a DOM representation of whatever there is in the xml:
#SuppressWarnings("unchecked")
public <T> T getOthersParsedAs(Class<T> clazz) throws JAXBException{
JAXBContext context = JAXBContext.newInstance(clazz);
Unmarshaller unmarshaller = context.createUnmarshaller();
T res = (T) unmarshaller.unmarshal((Node)others[0]);
if (res instanceof JAXBElement){
res = (T)JAXBIntrospector.getValue(res);
}
return res;
}
This way I cant get them with:
Specific spec = ...
SpecificInfo info = spec.getOthersParsedAs(SpecificInfo.class);
or:
AnotherInfo info = spec.getOthersParsedAs(AnotherInfo .class);
UPDATE:
I've done a method to insert any object to that xml in that node (ugly but shows all the code in same method):
public <T> void setOthersInXML(T data) throws JAXBException, ParserConfigurationException{
JAXBContext context = JAXBContext.newInstance(data.getClass());
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db= dbf.newDocumentBuilder();
Document document = db.newDocument();
Marshaller marshaller = context.createMarshaller();
marshaller.marshal(data, document);
others = new Object[]{document.getDocumentElement()};
}
And is used like a setter.
Edited again
Because I found a problem: if the class haven't well defined the XMLRootElement getOthersParsedAs will return you a JAXBElement object and this could be problematic, so I've added the check to the method
If I understand your need, you want to be able to get content of another elements into <specific/> ?
so maybe you can take a look to #XmlAnyElement depending on the structure your want to catch.
you might be interested in lax attribute aiming to preserve the processing of specificInfo and anotherInfo
#XmlAnyElement(lax="true")
public Object[] others;
Related
I need to transform the XML below to a POJO. I cannot modify the XML. My problem is the upsert object is always null.
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<MarginCall xmlns="http://my-name-space" type="Upsert">
<upsert>
<AccountId>ABCD</AccountId>
</upsert>
</MarginCall>
MarginCall Class
#XmlRootElement(name="MarginCall", namespace="http://my-name-space")
#XmlAccessorType(XmlAccessType.FIELD)
public class MarginCall {
#XmlAttribute
private String type;
#XmlElement
protected Upsert upsert;
// Getters, setters and ToString()
Upsert Class
#XmlType(name = "upsert", namespace="")
#XmlAccessorType(XmlAccessType.FIELD)
public class Upsert {
#XmlElement(name="AccountId")
private String accountId;
// Getter, setter and ToString()
How I transform the XML (received as String:message below)
JAXBContext jaxbContext;
jaxbContext = JAXBContext.newInstance(new Class[]{com.sandrew.MarginCall.class});
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
MarginCall marginCall = (MarginCall) jaxbUnmarshaller.unmarshal(new StringReader(message));
System.out.println(marginCall); // prints: MarginCall{type='Upsert', upsert=null} <---- PROBLEM
I tried to change, remove the namespace on Upsert, without luck. I have also created a MarginCall object, then tried to write it as an XML, and confirmed the result is structured as I want:
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
Upsert upsert = new Upsert();
upsert.setAccountId("ABCD");
MarginCall m = new MarginCall();
m.setType("T");
m.setUpsert(upsert);
jaxbMarshaller.marshal(m, new File("res.xml"));
// res.xml = <?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:MarginCall xmlns:ns2="http://my-name-space" type="T"><upsert><AccountId>ABCD</AccountId></upsert></ns2:MarginCall>
Any idea why when I go from String to POJO, I get null on the upsert object ?
In XML the namespace definition xmlns="http://my-name-space"
in the root element implicitly inherits to the nested elements.
However, in Java this implicit inheriting is not the case.
You need to code this explicitly.
Therefore you need to specify this namespace also for the properties
representing the non-root elements.
You do this by specifying the namespace in their
#XmlElement annotations.
In class MarginCall
#XmlElement(namespace="http://my-name-space")
protected Upsert upsert;
In class Upsert
#XmlElement(name="AccountId", namespace="http://my-name-space")
private String accountId;
After having done this, the output of the unmarshalled object will be
MarginCall(type=Upsert, upsert=Upsert(accountId=ABCD))
I'm trying to unmarshal the following XML using Jackson 2.4.0 XmlMapper:
<root>
<a/>
<b/>
</root>
...and the following POJO
class Root {
#JacksonXmlElementWrapper(useWrapping = false)
#JsonSubTypes({
#JsonSubTypes.Type(name = "a", value = POJO_A.class),
#JsonSubTypes.Type(name = "b", value = POJO_B.class)
})
public final List<AbstractPOJO> objects = new ArrayList<>();
}
I've also tried with JAXB #XmlElements annotations with the same result, which is:
Unrecognized field "a" (class Root), not marked as ignorable (1 known properties: "objects"])
So it seems that Jackson thinks my list is called "objects" instead of "a" and "b". Normally I fix that by using #JsonProperty("newName") but in this case I expected that to be handled by the #JsonSubtypes or #XmlElements annotations.
As I cannot modify the input XML, is there anything else I can do, either with Jackson, Jackson XML or JAXB annotations?
Update: Forgot to say, the problem is deserializing to the same collection (because I need to keep the order and they can be mixed). Doing it in separate fields works just fine.
When I was trying peeskillet answer and changed Jackson for JAXB unmarshaller, using #XmlElements/#XmlElementRefs worked (#XmlAnyElement did not for some reason, I was getting a list of ElementNSImpl instead of my own classes).
It will be nice to have this working with Jackson, but in the meantime, this is the way to make this work.
UPDATE:
What I did was:
class Root {
#XmlElements({
#XmlElement(name = "a", type = PojoA.class),
#XmlElement(name = "b", type = PojoB.class)
})
public final List<AbstractPOJO> objects = new ArrayList<>();
}
I also added a #XmlRootElement(name = "a") to PojoA, but I think it's not used when you have #XmlElements
"As I cannot modify the input XML, is there anything else I can do, either with Jackson, Jackson XML or JAXB annotations?"
I'm not too familiar with Jackson's Xml features. But with the JAXB Unmarshaller and a little change to your annotations, this can be achieved.
For your List<AbstractPojo> you can use the #XmlAnyElement(lax = true).
With lax = true:
If true, when an element matches a property marked with XmlAnyElement is known to JAXBContext (for example, there's a class with XmlRootElement that has the same tag name, or there's XmlElementDecl that has the same tag name), the unmarshaller will eagerly unmarshal this element to the JAXB object, instead of unmarshalling it to DOM
Which basically means that if we annotate PojoA and PojoB with #XmlRootElement and pass the element name as the name attribute (#XmlRootElement(name = "a")), by definition, this should work.
Let's give it a shot:
public abstract class AbstractPojo {
// note this class is not annotated. It will be known in the context
// as we're explicitly using the type int the Root class
}
#XmlRootElement(name = "a")
public class PojoA extends AbstractPojo {
}
#XmlRootElement(name = "b")
public class PojoB extends AbstractPojo {
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "root")
public class Root {
#XmlAnyElement(lax = true)
protected List<AbstractPojo> objects;
public List<AbstractPojo> getObjects() {
if (objects == null) {
objects = new ArrayList<>();
}
return this.objects;
}
}
Using the following xml file to test
<?xml version="1.0" encoding="UTF-8"?>
<root>
<a/> <b/> <b/> <a/> <a/> <b/> <b/>
</root>
And the following test program
public class JaxbTest {
private static final String FILE_NAME = "test.xml";
public static void main(String[] args) throws Exception {
JAXBContext context = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
File f = new File(FILE_NAME);
Root root = (Root)unmarshaller.unmarshal(f);
List<AbstractPojo> list = root.getObjects();
for (AbstractPojo p : list) {
System.out.print( p instanceof PojoA ? "a " : "b ");
}
System.out.println();
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
We get the result we're looking for.
a b b a a b b // along with the xml file content we marshal for testing
Here are some good resources:
JAXB tutorial
Oracle JAXB tutorial
#XmlAnyElement
Blaise Doughan's blog is always a good resource
UPDATE
Ok I see why you were getting ElementNSImpl. This doesn't work for me, I'm getting ElementNSImpl objects. Do you know what's happening?
Yea I see what's happening. Firstly, I compiled an xsd with xjc, and it created an ObjectFactory for me, which declared the elements. That's why it way working for me.
If you don't do this, then you should explicitly put the PojoA and PojoB into the context.
JAXBContext.newInstance(Root.class, PojoA.class, PojoB.class);
I have a situation here, trying to act as a gateway between two APIs. What I need to do, is:
make a request to an APIa;
parse (marshal) the XML response into an java object;
make little changes to it;
and then give a response in XML (unmarshal) to the other end (APIb).
The thing is that I use the same object to parse the API response and to send the response to the other end.
public class ResponseAPI{
#XmlElement(name="ResponseCode") //I receive <ResponseCode> but I need to send <ResultCode>
private String responseCode;
//getter and setter
}
as the comment says: I receive but I need to send
Is there a way to get this done without having to create another extra class which carries ResultCode?
thanks in advance!
You can try next solution using #XmlElements annotaion
#XmlAccessorType(XmlAccessType.FIELD)
public class ResponseAPI
{
#XmlElements(
{
#XmlElement(name = "ResponseCode"),
#XmlElement(name = "ResultCode")
})
private String responseCode;
// ...
}
In this case both ResponseCode and ResultCode will be used during unmarshalling (xml -> object) and only ResultCode during marshalling (object -> xml).
So you can unmarshall XML like
<responseAPI>
<ResponseCode>404</ResponseCode>
</responseAPI>
After marshalling object will looks like
<responseAPI>
<ResultCode>404</ResultCode>
</responseAPI>
Note:
The answer given by Ilya works but isn't guaranteed to work across all implementations of JAXB or even across versions of a single JAXB implementation. The #XmlElements annotation is useful when the decision of which element to marshal depends on the type of the value (see: http://blog.bdoughan.com/2010/10/jaxb-and-xsd-choice-xmlelements.html). In your use case both the ResponseCode and ResultCode elements correspond to type String, unmarshalling will always work fine, but the choice of which element to output is arbitrary. Some JAXB Impls may have last specified wins, but others could easily have first wins.
You could do the following by leveraging #XmlElementRef.
Java Model
ResponseAPI
We will change the responseCode property from type String to JAXBElement<String>. The JAXBElement allows us to store the element name as well as the value.
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class ResponseAPI{
#XmlElementRefs({
#XmlElementRef(name = "ResponseCode"),
#XmlElementRef(name = "ResultCode")
})
private JAXBElement<String> responseCode;
public JAXBElement<String> getResponseCode() {
return responseCode;
}
public void setResponseCode(JAXBElement<String> responseCode) {
this.responseCode = responseCode;
}
}
ObjectFactory
The #XmlElementRef annotations we used on the ResponseAPI class correspond to #XmlElementDecl annotations on a class annotated with #XmlRegistry. Traditionally this class is called ObjectFactory but you can call it anything you want.
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
#XmlElementDecl(name="ResponseCode")
public JAXBElement<String> createResponseCode(String string) {
return new JAXBElement<String>(new QName("ResponseCode"), String.class, string);
}
#XmlElementDecl(name="ResultCode")
public JAXBElement<String> createResultCode(String string) {
return new JAXBElement<String>(new QName("ResultCode"), String.class, string);
}
}
Demo Code
input.xml
<responseAPI>
<ResponseCode>ABC</ResponseCode>
</responseAPI>
Demo
When creating the JAXBContext we need to ensure that we include the class that contains the #XmlElementDecl annotations.
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(ResponseAPI.class, ObjectFactory.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("Scratch/src2/forum24554789/input.xml");
ResponseAPI responseAPI = (ResponseAPI) unmarshaller.unmarshal(xml);
ObjectFactory objectFactory = new ObjectFactory();
String responseCode = responseAPI.getResponseCode().getValue();
JAXBElement<String> resultCodeJAXBElement = objectFactory.createResultCode(responseCode);
responseAPI.setResponseCode(resultCodeJAXBElement);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(responseAPI, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<responseAPI>
<ResultCode>ABC</ResultCode>
</responseAPI>
I need to marshal an collection List to xml but I want the user to choose which fields will be marshaled from the List's objects. Here is a better explaination of what I am trying to do.
First I have an POJO called Server
#XmlAccessorType(XmlAccessType.FIELD)
public class Server {
#XmlElement(nillable=true) private String vendor;
#XmlElement(nillable=true) private int memory;
#XmlElement(nillable=true) private String cpu;
//getters and setters
}
Then in my applications I have this method which is doing the marshal job.
Note that this implementation is based on this Creating a Generic List Wrapper in JAXB
and MOXy's Object Graphs - Partial Models on the Fly to/from XML & JSON
protected void marshalCollection(Object collection,OutputStream os) throws Exception {
//The output list contains the name of the Server.class fields to be
//marshaled
List<String> output = (List<String>) getContext()
.getHttpServletRequest().getAttribute("output");
Class<?> cls = Class.forName("my.package.Server");
//This list contains the Server objects to be marshaled
List ls = (List) collection;
JAXBContext jc = JAXBContext.newInstance(Wrapper.class, cls);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
QName qName = new QName("servers");
Wrapper wrapper = new Wrapper(ls);
JAXBElement<Wrapper> jaxbElement = new JAXBElement<Wrapper>(qName,
Wrapper.class, wrapper);
ObjectGraph outputInfo = JAXBHelper.getJAXBContext(jc)
.createObjectGraph(Wrapper.class);
Subgraph subg = outputInfo.addSubgraph("server");
for (String o : output) {
subg.addAttributeNodes(o);
}
marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, outputInfo);
marshaller.marshal(jaxbElement, os);
}
Here is my Wrapper class
public class Wrapper<T> {
#XmlAnyElement(lax=true)
private List<T> items;
public Wrapper() {
items = new ArrayList<T>();
}
public Wrapper(List<T> items) {
this.items = items;
}
public List<T> getItems() {
return items;
}
}
Unfortunately I get back an empty xml
<?xml version="1.0" encoding="UTF-8"?>
<servers/>
An example of what I want to get back is below
If the output list contains "memory" and "cpu" and the ls list contains two Server objects
Server[vendor=HP,memory=4096,cpu=intel]
Server[vendor=IBM,memory=2048,cpu=amd]
then the xml that I want to get back is
<?xml version="1.0" encoding="UTF-8"?>
<servers>
<server>
<memory>4096</memory>
<cpu>intel</cpu>
</server>
<server>
<memory>2048</memory>
<cpu>amd</cpu>
</server>
</servers>
Can anyone help me find what I am doing wrong?
Is there any other way that I could marshal only selected fields from my Server objects in the List?
Thanks
You have hit a bug in how we handle Object Graphs with #XmlAnyElement(lax=true) in MOXy. You can use the following bug to track our progress on this issue.
http://bugs.eclipse.org/423953
Workaround
You could leverage JAXB with StAX for this use case. StAX will be used to write the wrapper element, and then you can marshal the individual instances of Server to it. Note how the Object Graph is created changes slightly.
XMLOutputFactory xof = XMLOutputFactory.newFactory();
XMLStreamWriter xsw = xof.createXMLStreamWriter(System.out);
xsw.writeStartDocument();
xsw.writeStartElement("servers");
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
ObjectGraph outputInfo = JAXBHelper.getJAXBContext(jc)
.createObjectGraph(Server.class);
for (String o : output) {
outputInfo.addAttributeNodes(o);
}
marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, outputInfo);
for(Object item : ls) {
marshaller.marshal(item, xsw);
}
xsw.writeEndElement();
xsw.writeEndDocument();
xsw.close();
First. Sorry for bad english.
I want to make some "common" transformation of Map to XML according to given XSD in that way:
key of the Map will be equal to tag name in XML
tag names in XML will not be duplicated in different nodes (levels)
value in Map can contain for example List of Map that represent repeatable tags in the node
created xml have to accord an xsd.
etc.
So I am looking for a competent way to realize that.
Is there anybody who worked with similar tasks and can help me?
Any advise will appreciated. Thanks in advance!
P.S. Example.
Map:
"fname" : "Asdf"
"lname" : "Fdsa"
"cars" : "car" {"car1", "car2", "car3"}
XML:
<fname>Asdf</fname>
<lname>Fdsa</lname>
<cars>
<car>car1</car>
<car>car2</car>
<car>car3</car>
</cars>
First, you need one single root element. This is the requirement of XML syntax.
Now you can use JAXB. Define you class Data:
#XmlType
public class Data {
private String fname;
private String lname;
private Collection<String> cars;
// getters
public String getFname() {
return fname;
}
public String getLname() {
return lname;
}
#XmlElementWrapper(name = "cars")
#XmlElement(name = "car")
public String getCars() {
return cars;
}
// setters.....
}
Now your can create instance of this class instance, call all setters to fill the data and then call:
JAXBContext ctx = JAXBContext.newInstance("com.yourpackage");
Marshaller m = ctx.createMarshaller();
m.marshal(data, System.out);
And you will see your data serialized as XML on STDOUT.
To parse XML back say:
JAXBContext ctx = JAXBContext.newInstance("com.panpwr.api.model.deployment");
Unmarshaller unmarshaller = ctx.createUnmarshaller();
Data data = (Data)unmarshaller.unmarshal(in); // in is the input stream for XML
// now your instance of data is populated from XML