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
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 need to unmarshall the following xml String named retornoExtrato in my code
<?xml version="1.0" encoding="UTF-8" ?>
<extrato xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<erro>
<codigo/>
<descricao/>
</erro>
<consultaextrato>
<header><![CDATA[SOME MULTIPLE
LINES HEADER]]></header>
<body><![CDATA[SOME MULTIPLE
LINES BODY]]></body>
<trailer><![CDATA[SOME MULTIPLE
LINES TRAILER]]></trailer>
</consultaextrato>
</extrato>
into an Extrato object, here are my classes (constructors, getters and setters ommited when default)
#XmlRootElement(name = "extrato")
public class Extrato {
private Erro erro;
private ConsultaExtrato consultaExtrato;
}
#XmlRootElement(name = "erro")
public class Erro {
private String codigo;
private String descricao;
}
#XmlRootElement(name = "consultaextrato")
public class ConsultaExtrato {
private String header;
private String body;
private String trailer;
#XmlCDATA
public String getHeader() {
return header;
}
#XmlCDATA
public String getBody() {
return body;
}
#XmlCDATA
public String getTrailer() {
return trailer;
}
}
The situation is when unmarshalling:
Erro always get umarshelled
ConsultaExtrato is getting null
Unmarshaller jaxbUnmarshaller = JAXBContext.newInstance(Extrato.class).createUnmarshaller();
Extrato extrato = (Extrato) jaxbUnmarshaller.unmarshal(new StringReader(retornoExtrato));
On the other hand, if I create a xml with only the consultaextrato tag, it gets unmarshelled ok. But it doesn't seems to work as an inner tag.
I've tried some extra jaxb annotation in all classes, none worked. What am I missing here?
You need to tell JAXB that the XML element <consultaextrato>
within the <extrato> element corresponds to
the Java property consultaExtrato in your Extrato class.
You do this by annotating this property (or rather its getter or setter method)
with #XmlElement and giving the XML name there:
#XmlElement(name = "consultaextrato")
If you don't do this, then JAXB would derive the XML element name
from the Java property name (i.e. consultaExtrato) and thus
get no match because of the different spelling.
And by the way: The #XmlRootElement(name = "consultaextrato")
has an effect only if the <consultaextrato> is the root element
of your XML content, but not if <consultaextrato> is a nested
element within another element (in your case within <extrato>
element).
Thank you for taking the time to read.
My goal is to deserialize the response from an API request into 2 usable java objects.
I am sending an POST request to an endpoint to create a job in our schedule. The job is created successfully and the following XML is returned in the body:
<entry xmlns="http://purl.org/atom/ns#">
<id>0</id>
<title>Job has been created.</title>
<source>com.tidalsoft.framework.rpc.Result</source>
<tes:result xmlns:tes="http://www.auto-schedule.com/client">
<tes:message>Job has been created.</tes:message>
<tes:objectid>42320</tes:objectid>
<tes:id>0</tes:id>
<tes:operation>CREATE</tes:operation>
<tes:ok>true</tes:ok>
<tes:objectname>Job</tes:objectname>
</tes:result>
</entry>
When I attempt to only capture the elements id, title, and source the data is captured successfully. The problem is when I introduce the child object, which attempts to capture the data in the tes:result element.
Here's what the parent POJO looks like:
#XmlRootElement(name = "entry")
#XmlAccessorType(XmlAccessType.FIELD)
public class Response {
private String id;
private String title;
private String source;
ResponseDetails result;
public Response() {}
}
and here is the child object:
#XmlAccessorType(XmlAccessType.FIELD)
public class ResponseDetails {
#XmlElement(name = "tes:message")
String message;
#XmlElement(name = "tes:objectid")
String objectid;
#XmlElement(name = "tes:operation")
String operation;
#XmlElement(name = "tes:ok")
String ok;
#XmlElement(name = "tes:objectname")
String objectname;
}
Finally, here is the package-info.java file I am using:
#XmlSchema(
namespace = "http://purl.org/atom/ns#",
elementFormDefault = XmlNsForm.QUALIFIED)
package com.netspend.raven.tidal.request.response;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
Any ideas are greatly appreciated. Thanks.
The problem is: Your Java code doesn't correctly specify the namespaces
corresponding to the inner XML element
<tes:result xmlns:tes="http://www.auto-schedule.com/client">
<tes:message>Job has been created.</tes:message>
<tes:objectid>42320</tes:objectid>
<tes:id>0</tes:id>
<tes:operation>CREATE</tes:operation>
<tes:ok>true</tes:ok>
<tes:objectname>Job</tes:objectname>
</tes:result>
The <tes:result> XML element has the namespace "http://www.auto-schedule.com/client".
The namespace prefix tes: itself is irrelevant for Java.
(XML namespace prefixes were invented only for better readability of the XML code.)
Therefore in your Response class, instead of just writing
ResponseDetails result;
you need to write
#XmlElement(namespace = "http://www.auto-schedule.com/client")
ResponseDetails result;
See also the javadoc of XmlElement.namespace.
Also all the XML sub-elements within the <tes:result> are specified
with this namespace "http://www.auto-schedule.com/client".
Therefore, also inside your ResponseDetails you have to correct
the namespace stuff. For example, instead of
#XmlElement(name = "tes:message")
String message;
you need to write
#XmlElement(name = "message", namespace = "http://www.auto-schedule.com/client")
String message;
You may also omit the name = "message" and simply write
#XmlElement(namespace = "http://www.auto-schedule.com/client")
String message;
because JAXB picks up this name from your Java property name message.
And similar for all the other properties in this class.
I am trying to programmatically create XML elements using JAXB in Java. Is this possible? I am reading this page here for something I can use, but have so far found nothing.
Usually you start by defining a bean
#XmlRootElement public class MyXML {
private String name;
public String getName() { return name; }
#XmlElement public void setName(String s) { this.name = s; }
}
and serialize it with code like
public class Serializer {
static public void main(String[] args) {
MyXML m = new MyXML();
m.setName("Yo");
JAXBContext jaxbContext = JAXBContext.newInstance(MyXML.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(m, new File("MyXML_"+ ".xml"));
}
}
that whould produce the following XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<myXML>
<name>Yo</name>
</myXML>
How would I program my Java class to create the element tag name depending on what is entered in the program? For instance in my example the tag element is called 'name'. How could I set this at runtime though? Is this possible with generics or some other way?
The B in JAXB stands for Bean so no, there's no way to use JAXB without defining beans.
You just want to dinamically create an XML so take a look at jOOX for example (link to full Gist)
Document document = JOOX.builder().newDocument();
Element root = document.createElement("contacts");
document.appendChild(root);
for (String name : new String[]{"John", "Jessica", "Peter"}) {
$(root).append(
$("contact"
, $("name", name)
, $("active", "true")
)
);
}
Here, you use annotation before compile-time while you have no knowledge yet of the format you will need.. Marshalling this way is not that different from serializing, and it basically map directly the fields of a java object to an XML representation --> (if something is not defined in the object, it won't appear in the representation).
What you thrive to do looks like simple xml crafting (a XML parser would be enough S(t)AX/DOM whatever -- I like Jackson).
For the sake of curiosity, if you really want to fiddle with annotation you can use a bit of reflection in conjonction with the answer you will find here
I have a class to map into XML using xstream.
Class is having 5 fields. Out of these five fields I want one field that should not be mapped to xml and only four fields should be mapped into XML.
E.g
public class Person {
private String firstname;
private String lastname;
private String phone;
// ... constructors and methods
}
When this class is mapped I want XML like this i.e. no phone number
<person>
<firstname>Joe</firstname>
<lastname>Walnes</lastname>
</person>
xstream.omitField(Person.class, "phone");
or in annotation
With classic Java code:
XStreamMarshaller marshaller = ... //Get your XStreamMarshaller
marshaller.getXStream().omitField(Person.class, "phone");
With annotation:
#XStreamOmitField
private String phone;
For the second case, you must call marshaller.getXStream().autodetectAnnotations(true); or marshaller.getXStream().processAnnotations(Person.class); to be sure that annotations are used.