How to map XML to POJO - java

I have the following result:
<Result xmlns="urn:buscape" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" totalResultsAvailable="1" totalResultsReturned="1" totalPages="1" page="1" totalLooseOffers="0" xsi:schemaLocation="http://developer.buscape.com/admin/lomadee.xsd">
<details>
<applicationID>999999999999999</applicationID>
<applicationVersion>1.0</applicationVersion>
<applicationPath/>
<date>2016-09-12T23:50:19.722-03:00</date>
<elapsedTime>19</elapsedTime>
<status>success</status>
<code>0</code>
<message>success</message>
</details>
<lomadeeLinks>
<lomadeeLink>
<id>1</id>
<originalLink>link</originalLink>
<redirectLink>link2</redirectLink>
<code>0</code>
</lomadeeLink>
</lomadeeLinks>
</Result>
Looking at "2.7.4 Retrieving XML data via HTTP GET", I have to map this XML to POJO-like object in Java, here is the question, I can't find the #Root and #Element annotation and I'm not sure how to correctly map the XML into a Java object.

If you are using spring/springboot then simply you can use
(YourPOJO)getWebServiceTemplate().marshalSendAndReceive(yourSOAPService);
POJO :-
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"getOrdersResponse"
})
#XmlRootElement(name = "nameOfTag", namespace = "http://example.org/yourResource")
public class GetOrdersByDateResult {
#XmlElement(name = "GetOrdersResponse")
protected GetOrdersResponseType getOrdersResponse;
getter();
setter();
}

Related

XMLDecoder >> java.lang.IllegalArgumentException: Unsupported element

Got this error
java.lang.IllegalArgumentException: Unsupported element: net
from this example xml file
<?xml version="1.0" encoding="UTF-8"?>
<net>
<node label="A">
...
</node>
<node label="B">
...
</node>
<node label="C">
...
</node>
</net>
with these java code lines
...
FileInputStream file = new FileInputStream("example.xml");
XMLDecoder decoder = new XMLDecoder(file);
Object decodedResistors = (Object) decoder.readObject();
file.close();
...
Do not use java.beans.XMLDecoder for deserialisation custom XML payloads. It was not designed for that. Read article Long Term Persistence of JavaBeans Components: XML Schema. It contains some example XML payloads which can be deserialised back by XMLDecoder:
<?xml version="1.0" encoding="UTF-8" ?>
<java version="1.4.0" class="java.beans.XMLDecoder">
<void id="myController" property="owner"/>
<object class="javax.swing.JButton">
<void method="addActionListener">
<object class="java.beans.EventHandler" method="create">
<class>java.awt.event.ActionListener</class>
<object idref="myController"/>
<string>doIt</string>
</object>
</void>
</object>
</java>
If you need to deserialise custom XML use JAXB or Jackson XML. You need to create a POJO model with JAXB annotations:
#XmlRootElement(name = "net")
#XmlAccessorType(XmlAccessType.FIELD)
class Net {
#XmlElement(name = "node")
private List<Node> nodes;
// getters, setters, toString
}
#XmlAccessorType(XmlAccessType.FIELD)
class Node {
#XmlAttribute
private String label;
// getters, setters, toString
}
Example usage:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
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;
import java.io.File;
import java.util.List;
public class JaxbApp {
public static void main(String[] args) throws Exception {
File xmlFile = new File("./resource/test.xml").getAbsoluteFile();
JAXBContext jaxbContext = JAXBContext.newInstance(Net.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Object net = unmarshaller.unmarshal(xmlFile);
System.out.println(net);
}
}
prints:
Net{nodes=[Node{label='A'}, Node{label='B'}, Node{label='C'}]}
See also:
java.lang.IllegalArgumentException: Unsupported element: rss

jaxb, separate namespace for child element

I am trying to create XML using jaxb like below format, where child element has separate name space.
<soap:Envelope xmlns:soap="http://demo.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Header>
<element1 xmlns="http://childnamespacehere">
<att1>test</att1>
<att2>test</att2>
</element1>
</soap:Header>
<soap:Body>
<element2 xmlns="http://childnamespacehere">
<att1>test</att1>
<att2>test</att2>
</element2 >
</soap:Body>
</soap:Envelope>
my class
#XmlRootElement(name = "soap:Envelope", namespace = "http://schemas.xmlsoap.org/soap/envelope/")
public class Envelope
private Element1 element1;
private Element2 element2;
#XmlElementWrapper(name = "soap:Header")
#XmlElement(name = "Element1", namespace = "http://childelementnamespace/")
public void setElement1(Element1 element){ }
#XmlElementWrapper(name = "soap:Body")
#XmlElement(name = "Element2" , namespace = "http://childelementnamespace/")
public void setElement2(Element2 element){ }
but i am getting xml generated like below, where child schema is at root level.
<soap:Envelope xsi:schemaLocation="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns2="http://childelementnamespace/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Header>
<ns2:Element1>
<att1>value</att1>
<att2>value</att2>
</ns2:Element1>
</soap:Header>
<soap:Body>
<ns2:Element2>
<att1>value</att1>
<att2>value</att2>
</ns2:Element2>
</soap:Body>
</soap:Envelope>
i have #xmlschema defined in package-info.java
#XmlSchema(namespace = "http://schemas.xmlsoap.org/soap/envelope/",
xmlns = { #javax.xml.bind.annotation.XmlNs(prefix = "Element1", namespaceURI = "http://childelementnamespace"),
#javax.xml.bind.annotation.XmlNs(prefix = "Element2", namespaceURI = "http://childelementnamespace") },
elementFormDefault = XmlNsForm.QUALIFIED)
package com.model;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
when i generate xml , name space for child elements are not getting generated , i only get namespace for root element.
i have solved by adding "xmlns" attribute to objects (childnode) Element1 and Element2.
class Elemenet1
#XmlAttribute(name="xmlns")
String xmlns = "http://childnamespacehere";
public void setXmlns(String namespace){};
public String getXmlns(){};
Output
<soap:Envelope xmlns:soap="http://demo.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Header>
<element1 xmlns="http://childnamespacehere">
<att1>test</att1>
<att2>test</att2>
</element1>
</soap:Header>
<soap:Body>
<element2 xmlns="http://childnamespacehere">
<att1>test</att1>
<att2>test</att2>
</element2 >
</soap:Body>
</soap:Envelope>
In where you say what is being generated is has xmlns:ns2="http://childelementnamespace/" up the top, this is declaring the namespace and used in this fashion <ns2:Element2> using ns2 here uses tha namspace declared previously.
So what you are expecting and what you are getting are the exact same just declared in different places, the jaxB method is more correct as it is not declaring the same namespace more than once.
This is a temporary solution. This triggers major problems when you want to unmarshall a xml document.
But you can use different packages for marshall and unmarshall process too.

Prevent XXE Attack with JAXB

Recently, we had a security audit on our code, and one of the problem is that our application is subject to the Xml eXternal Entity (XXE) attack.
Basically, the application is a calculator that receives inputs as XML, through a Web-Service.
Here is an example of such an XXE attack on our application:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/>
<soapenv:Body>
<foo:calculateStuff>
<!--Optional:-->
<xmlInput><![CDATA[<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE currency [
<!ENTITY include SYSTEM "file:///d:/" >]>
<calcinput>...</calcinput>
]]></xmlInput>
</foo:calculateStuff>
</soapenv:Body>
</soapenv:Envelope>
As you can see, we can refer to an entity that points to an external file ("file:///d:/").
Regarding the XML input itself (the <calcinput>...</calcinput> part) is unmarshalled with JAXB (v2.1). The web-service part is based on jaxws-rt (2.1).
What do I need to do to secure my web-service?
JAXB
You can prevent the Xml eXternal Entity (XXE) attack by unmarshalling from an XMLStreamReader that has the IS_SUPPORTING_EXTERNAL_ENTITIES and/or XMLInputFactory.SUPPORT_DTD properties set to false.
JAX-WS
A JAX-WS implementation should take care of this for you. If it doesn't I would recommend opening a bug against the specific implmententation.
EXAMPLE
Demo
package xxe;
import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Customer.class);
XMLInputFactory xif = XMLInputFactory.newFactory();
xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
XMLStreamReader xsr = xif.createXMLStreamReader(new StreamSource("src/xxe/input.xml"));
Unmarshaller unmarshaller = jc.createUnmarshaller();
Customer customer = (Customer) unmarshaller.unmarshal(xsr);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(customer, System.out);
}
}
input.xml
This XML document contains an entity that has been setup to get the listing of files I used to create this example.
<?xml version="1.0"?>
<!DOCTYPE customer
[
<!ENTITY name SYSTEM "/Users/bdoughan/Examples/src/xxe/">
]
>
<customer>
<name>&name;</name>
</customer>
Customer
package xxe;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Customer {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Output - Default Configuration
By default the entity will be resolved.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
<name>Customer.java
Demo.java
input.xml
</name>
</customer>
Output when XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES property is set to false
When this property is set the entity is not resolved.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
<name></name>
</customer>
Output when XMLInputFactory.SUPPORT_DTD property is set to false
When this property is set an exception is thrown trying to resolve the entity.
Exception in thread "main" javax.xml.bind.UnmarshalException
- with linked exception:
[javax.xml.stream.XMLStreamException: ParseError at [row,col]:[8,15]
Message: The entity "name" was referenced, but not declared.]
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.handleStreamException(UnmarshallerImpl.java:436)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:372)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:342)
at xxe.Demo.main(Demo.java:18)
Caused by: javax.xml.stream.XMLStreamException: ParseError at [row,col]:[8,15]
Message: The entity "name" was referenced, but not declared.
at com.sun.org.apache.xerces.internal.impl.XMLStreamReaderImpl.next(XMLStreamReaderImpl.java:598)
at com.sun.xml.bind.v2.runtime.unmarshaller.StAXStreamConnector.bridge(StAXStreamConnector.java:196)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:370)
... 2 more

Building an XML response with Jersey

I'm attempting to build a Jersey web service which will take in data, format it into an XML document, then pass it to another service. I realize that Jersey does have XML support, but I'm having a bit of trouble implementing it due to the required XML structure for the project. The desired output looks something like this:
<root-element>
<table>
<row>
<d>data1</d>
<d>data2</d>
<d>data3</d>
</row>
<row>
<d>data4</d>
<d>data5</d>
<d>data6</d>
</row>
</table>
My issue arises in that there are a variable number of <d> and <row> elements, which will be determined based on the data passed in. I know that I can format a simple table with #XmlRootElement above the class which handles the data, but this may only be useful for my <root-element> since the element only gets populated with other elements. I know I'll need to use some sort of loop to create each <row>, but I'm not sure how I can create each <d> element with different data in each field. Any suggestions?
You can use a Java model with JAXB (JSR-222) annotations to support your use case. Elements that can occur more than once will correspond to List properties in your Java model. Below is an example of how your document could be mapped.
Table
We will use the #XmlElementWrapper annotation to add a grouping element, and the #XmlElement annotation to set the element name for the items in the collection.
package forum11543081;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="root-element")
#XmlAccessorType(XmlAccessType.FIELD)
public class Table {
#XmlElementWrapper(name="table")
#XmlElement(name="row")
private List<Row> rows;
}
Row
If the name of your property/field matches the name of the resulting XML element then you do not require any annotations.
package forum11543081;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Row {
private List<String> d;
}
Demo
Below is a standalone example to prove that the mapping works:
package forum11543081;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Table.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum11543081/input.xml");
Table table = (Table) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(table, System.out);
}
}
input.xml/Output
<root-element>
<table>
<row>
<d>data1</d>
<d>data2</d>
<d>data3</d>
</row>
<row>
<d>data4</d>
<d>data5</d>
<d>data6</d>
</row>
</table>
</root-element>
For More Information
http://blog.bdoughan.com/2010/09/jaxb-collection-properties.html
http://blog.bdoughan.com/2010/08/creating-restful-web-service-part-15.html
If you want to use the default Jersey/JAXB marshalling into XML, you would build a schema reflecting the structure you have indicated which includes collections (unbounded elements) and generate (using xjc) the corresponding java classes. The response from your restful service would be the type associated with the root element and you would build the structure as part of the service. The unbounded elements are rendered as java lists so they can be of arbitrary number of elements. In the code you would just .add(element) as necessary. Something like:
<schema ...>
...
<element name="root-element">
<complexType>
<sequence>
<element name="table" type="tns:TableType" />
</sequence>
</complexType>
</element>
<complexType name="TableType">
<sequence>
<element name="row" minOccurs="0" maxOccurs="unbounded" type="tns:RowType" />
</sequence>
</complexType>
<complexType name="RowType">
<sequence>
<element name="d" minOccurs="0" maxOccurs="unbounded" type="string" />
</sequence>
</complexType>
</schema>
The alternate approach would be (as mentioned by TedTrippin) using stax (streaming processor) to build up the xml document tag by tag with loops in appropriate places and returning the final result.
What I've ended up doing:
Since I had code from another project I could re-use for looping through the XML building, I decided to build the XML in a document, then write that document to a string like so:
public class XmlHandler{
public static String buildXml(){
String xmlString="";
//Create XML Document
DocumentBuilderFactory docfac = DocumentBuilderFactory.newInstance();
DocumentBuilder docbuil = null;
docbuil = docfac.newDocumentBuilder();
Document doc = docbuil.newDocument();
//Build XML Elements
Element root = doc.createElement("root-element");
doc.appendChild(root);
Element table = doc.createElement("table");
root.appendChild(table);
//Hard coded data here for testing purposes.
String[][]array={
{"data1", "data2", "data3"},
{"data4", "data5", "data6"}
};
Text text = null;
Element d = null;
Element row = null;
for(String[] line : array)
{
row=doc.createElement("row");
table.appendChild(row);
for(String label : line)
{
d = doc.createElement("d");
row.appendChild(d);
text = doc.createTextNode(label);
d.appendChild(text);
}
}
}
//Write Document to String
DOMImplementationLS domImplLS = (DOMImplementationLS) doc.getImplementation();
LSSerializer serializer = domImplLS.createLSSerializer();
serializer.getDomConfig().setParameter("format-pretty-print", true);
LSOutput output = domImplLS.createLSOutput();
output.setEncoding("UTF-8");
StringWriter sw = new StringWriter();
output.setCharacterStream(sw);
serializer.write(doc, output);
xmlString = sw.toString();
return xmlString;
}
}
While the hard coded String array won't be around for long, just until I find out what data types I need to pass in, this class is doing the trick just fine.

From list of key values to attribute key=value

I have a question about JAXB. Basically what I have are these two classes:
Element {
String name
List<Attribute> attributes;
}
Attribute {
String key
String value
}
Of course with getters and setters, and with JAXB XmlRootElement.
The XML generated from this is:
<element>
<attributes>
<key>id</key>
<value>1</value>
</attributes>
<name>My Element</name>
</element>
But what I'm looking for is something more like this:
<element id="1">
<name>My Element</name>
</element>
That is, for each instance of Attribute, I want key=value (as an attribute)
Is this possible in JAXB?
Regards,
Morten
I do not think that it will work with a List. But there is an alternative using a
Map and
#XmlAnyAttribute
Your example:
#XmlRootElement
public static class Element
{
#XmlElement
String name;
#XmlAnyAttribute
Map<QName, Object> map;
}
{
//
Element element = new Element();
element.name = "a wonderful name";
element.map = new HashMap<QName, Object>();
element.map.put( new QName( "id" ), "1" );
element.map.put( new QName( "other" ), "2" );
}
Result of that:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<element id="1" other="2">
<name>a wonderful name</name>
</element>
Best regards!

Categories