From list of key values to attribute key=value - java

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!

Related

How to map XML to POJO

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();
}

JAXB adapter which return xml node or raw xml text instead object

Is it possible:
Class Qwe {
#SuperAdapter
Map<String, String> prms
}
after marshalling:
<qwe>
<prms>
<prm>
<name>qwe</name>
<val>zxc</val>
</prm>
<prm>
<name>tyu</name>
<val>ghj</val>
</prm>
...
</prms>
</qwe>
I can solve this problem if I have access to the DOM or insert ram xml part.
Or advise me the best solution.
I can solve it via subclasses but I do not think that is a good solution.
Please see this post by Blaise Doughan. Snippets:
package blog.map;
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Customer {
private Map<String, Address> addressMap = new HashMap<String, Address>();
#XmlElementWrapper(name="addresses")
public Map<String, Address> getAddressMap() {
return addressMap;
}
public void setAddressMap(Map<String, Address> addressMap) {
this.addressMap = addressMap;
}
}
Output:
<?xml version="1.0" encoding="UTF-8"?>
<customer>
<addresses>
<entry>
<key>shipping</key>
<value>
<street>2 B Road</street>
</value>
</entry>
<entry>
<key>billing</key>
<value>
<street>1 A Street</street>
</value>
</entry>
</addresses>
</customer>

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.

JAXB unmarshal has always null value for nested object

I have a web-service, defined by writing its WSDL and underlaying XSD, and the java server code classes / java bindings were generated using JAXB/xjc.
Everything looks fine service is running properly... but for every request (looking well-formed after receiving when looking on log-output) the nested elements seem to be always null when accessing through my java code.
Can someone figure out why customerId.getCustomer() always returns null?
My XSD (partly):
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns:tip="http://example.org/tip" xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://example.org/tip/pro">
<complexType name="id">
<attribute name="id" type="int" use="required"/>
<attribute name="name" type="string" use="optional"/>
</complexType>
<complexType name="customer_id">
<sequence>
<element name="customer" type="tip:id" minOccurs="0"/>
</sequence>
</complexType>
<element name="get_customer_request" type="tip:customer_id"/>
</schema>
The generated class CustomerId:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "customer_id", propOrder = {"customer"})
public class CustomerId {
protected Id customer;
public Id getCustomer() {
return customer;
}
public void setCustomer(Id value) {
this.customer = value;
}
}
The generated class for Id look similar, I don't think there is something special.
In my request handler I got the following extract:
Handler:
JAXBElement<?> request = requestHandler.unmarshallRequest(inputStream);
Object jaxbClass = request.getDeclaredType();
expectedClass = CustomerId.class;
// next line does not throw exception with given XML
if (jaxbClass != expectedClass) throw new IllegalArgumentException();
CustomerId customerId = (CustomerId)request.getValue();
if (customerId == null) {
logInfo("customerId: null");
} else if (customerId.getCustomer() == null) {
// this is the part that always will be executed... why?
logInfo("customerId.customer: null");
} else {
logInfo("customer id: " + customerId.getCustomer().getId());
// return mbean.getCustomer(customerId);
}
And finally an example request XML:
<?xml version="1.0" encoding="ISO-8859-1"?>
<m:get_customer_request xmlns:m="http://example.org/tip/pro">
<customer id="0" name="help"/>
</m:get_customer_request>
I stripped out SOAP envelope and body tags, since this is not causing any trouble.
Can anyone see, what I am doing wrong? (I am pretty sure, I do...)
Thanks for your effords!
PART 1
When I create a new Id and set customerId.customer with this, the full
output is
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<get_customer_request xmlns="example.com/tip/pro">
<customer name="xy" id="1"/>
</get_customer_request>
Based on this information it appears that your JAXB mappings expect the customer element to be in the example.com/tip/pro namespace, and your request document should be:
<?xml version="1.0" encoding="ISO-8859-1"?>
<m:get_customer_request xmlns:m="http://example.org/tip/pro">
<m:customer id="0" name="help"/>
</m:get_customer_request>
PART 2
When putting m: prefix to customer element in my request, the parser
complains that he found m:customer and expected customer.
This means that your XML schema does not match your mappings. If you expect the customer element to be in the namespace you can change your XML schema to the following:
<?xml version="1.0" encoding="UTF-8"?>
<schema
xmlns:tip="http://example.org/tip"
xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.org/tip/pro"
elementFormDefault="qualified">
...
</schema>
For more information on JAXB and namespaces see:
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html

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.

Categories