Producing XML of list of object which contains list - java

I have object structure in which I have a list of Layer1 elements and this Layer1 contains a list of Layer2 arguments like below (getter, setters, methods ommited):
#XmlRootElement(name = "root")
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlElement(name = "layer1")
private List<Layer1> layer1 = new ArrayList<>();
}
#XmlRootElement(name = "layer1")
#XmlAccessorType(XmlAccessType.FIELD)
public class Layer1 {
#XmlElement(name = "layer2")
private List<String> layer2;
}
I am trying to marshall this into XML in this way:
public void marshall(){
try {
JAXBContext jaxbContext = JAXBContext.newInstance(Root.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(root.getlayer1(), System.out);
}
But it's not working and generates an exception:
javax.xml.bind.JAXBException: class java.util.ArrayList nor any of its super class is known to this context.
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getBeanInfo(JAXBContextImpl.java:567)
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:467)
at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:308)
at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:236)
I want have below output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<layer1>
<layer2>value1</layer2>
<layer2>value12</layer2>
<layer2>value13</layer2>
</layer1>
<layer1>
<layer2>value123</layer2>
<layer2>value1231</layer2>
</layer1>
<layer1>
<layer2>value12</layer2>
<layer2>value1</layer2>
</layer1>
</root>
Please give me some advice how to annotate my case. I was reading other similar topics, but I still can't make a proper solution.

Related

Parsing wrapped attributes with jaxb

Given something like:
<root>
<wrapper>
<wrapped id="..."/>
<wrapped id="..."/>
</wrapper>
</root>
how can I map it to this POJO:
public class Root {
public Set<UUID> myIds = new LinkedHashSet();
}
I am wondering since #XmlElement( name = "wrapped" ) #XmlElementWrapper( name = "wrapper" ) works somewhat similar to what I want, is there something to get the attribute?
note: i am not using moxy so as far as I know, I cannot use xpaths. I am trying to avoid the #XmlJavaTypeAdapter route.
You need to modify your root class a little bit,
so that it will contain a Set<Wrapped> instead of a Set<UUID>.
#XmlRootElement(name = "root")
public class Root {
#XmlElementWrapper(name = "wrapper")
#XmlElement(name = "wrapped")
public Set<Wrapped> myWrappeds = new LinkedHashSet<>();
}
And you need a separate class for the <wrapped> elements.
Surprisingly for me, you don't need an #XmlJavaAdapter for id here, because JAXB already has a built-in converter between java.util.UUID and String.
public class Wrapped {
#XmlAttribute(name = "id")
public UUID id;
}
I have checked the above with this XML input file
<root>
<wrapper>
<wrapped id="550e8400-e29b-11d4-a716-446655440000"/>
<wrapped id="550e8400-e29b-11d4-a716-446655440001"/>
</wrapper>
</root>
and this main method which reproduces the original XML:
public static void main(String[] args) throws Exception {
JAXBContext context = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
File file = new File("root.xml");
Root root = (Root) unmarshaller.unmarshal(file);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}

JAX-B customized XML output

I'm working with the ECCP protocol in order to integrate my CRM with the Elastix Call Center Module. The protocol uses a XML structure defined as follows:
<request id="1">
<request_type> <!-- this will be mapped to the Java request class -->
<attributes>
</attributes>
</request_type>
</request>
and
<response id="1">
<response_type> <!-- this will be mapped to the Java response class -->
<attributes>
</attributes>
</response_type>
</response>
I'm using JAX-B to map XML to Java classes but the problem is that I have to put the JAX-B generated XML inside a <request></request> XML every request and extract the content from <response></response> in every response because the ECCP protocol defines that every request and response needs to nested to their respective elements.
Here's the code I'm using to do that:
document = createDocument();
Element requestWrapper = document.createElement("request");
requestWrapper.setAttribute("id", String.valueOf(wrapped.getId()));
document.appendChild(requestWrapper);
JAXBContext jc = JAXBContext.newInstance(wrapped.getClass());
Marshaller marshaller = jc.createMarshaller();
marshaller.marshal(wrapped, requestWrapper);
Exemplifying:
One of ECCP's protocol operation is JAX-B-mapped into a class like this (getters and setters were omitted):
#XmlRootElement(name = "loginagent")
#XmlAccessorType(XmlAccessType.FIELD)
public class EccpLoginAgentRequest implements IEccpRequest {
#XmlElement(name = "agent_number")
private String agentNumber;
#XmlElement(name = "password")
private String password;
}
And JAX-B outputs the following:
<loginagent>
<agent_number>username</agent_number>
<password>password</password>
</loginagent>
But what the ECCP's protocol requires is:
<request id="1"> <!-- id is an auto-increment number to identify the request -->
<loginagent>
<username>username</username>
<password>password</password>
</loginagent>
</request>
The question is: is there any other way to achieve in any other better way?
Thank you.
You can probably check out the #XmlSeeAlso annotation which will help you wrap the same content both for request and response. For the inner part you can create the separate class and map all the fields appropriately. I hope this helps you a little bit.
EDIT:
Sorry for the long response time. You need to create a wrapper class with the inner structure defined as an #XmlElement. Here's the way to achieve that XML structure:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "request")
public class RequestWrapper {
#XmlElement(name = "loginagent", required = true)
protected EccpLoginAgentRequest loginagent;
public EccpLoginAgentRequest getLoginagent() {
return loginagent;
}
public void setLoginagent(EccpLoginAgentRequest loginagent) {
this.loginagent = loginagent;
}
}
And here's the EccpLoginAgentRequest structure:
#XmlAccessorType(XmlAccessType.FIELD)
public class EccpLoginAgentRequest {
#XmlElement(name = "agent_number")
private String agentNumber;
#XmlElement(name = "password")
private String password;
// getters and setters omitted
}
So in result, you can print the XML you want like that:
JAXBContext jaxbContext = JAXBContext.newInstance(Wrapper.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
EccpLoginAgentRequest request = new EccpLoginAgentRequest();
request.setAgentNumber("1");
request.setPassword("pass");
Wrapper wrapper = new Wrapper();
wrapper.setLoginagent(request);
jaxbMarshaller.marshal(wrapper, System.out);
It will give you the following output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<request>
<loginagent>
<agent_number>1</agent_number>
<password>pass</password>
</loginagent>
</request>
I found a way to solve this in this post: XML element with attribute and content using JAXB
So I've mapped a EccpRequestWrapper object as the following:
#XmlRootElement(name = "request")
public class EccpRequestWrapper {
#XmlAttribute
private Long id;
#XmlAnyElement
private IEccpRequest request;
}
and then my request JAX-B outputs my request the way ECCP protocol requires.
The #XmlAttribute and #XmlAnyElement annotation did the trick.
<request id="1">
<login>
<username>user</username>
<password>****</password>
</login>
</request>
A good JAXB guide can be found here https://jaxb.java.net/guide/Mapping_interfaces.html

any way to convert xml to object in java?

I am trying to use a XML and access to all fields and data on an easy way, so, I decided to use JaxB , but I have no idea how to create all the classes for the objects, I tried manually like this.
#XmlRootElement(name = "Response")
public class Response {
#XmlElement(ns = "SignatureValue")
String signatureValue;
}
But I get an error on #XmlElement saying the annotation is disallowed for this location...
I checked other posts and they work great if I have something like Hellw but doesnt work with more complex formats, an example of first part of mine is like this
<?xml version="1.0" encoding="UTF-8"?><DTE xsi:noNamespaceSchemaLocation="http://www.myurl/.xsd" xmlns:gs1="urn:ean.ucc:pay:2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
any idea how to do all this??
Thanks in advance
EDIT:
I forgot to say, the XML is actually a String with the entire XML.
The #XmlElement annotation is valid on a field. If you have a corresponding property then you should annotate the class with #XmlAccessorType(XmlAccessType.FIELD) to avoid a duplicate mapping exception.
Java Model
Annotating the Field
#XmlRootElement(name = "Response")
#XmlAccessorType(XmlAccessType.FIELD)
public class Response {
#XmlElement(name = "SignatureValue")
String signatureValue;
public String getSignatureValue() {
return signatureValue;
}
public void setSignatureValue(String signatureValue) {
this.signatureValue = signatureValue;
}
}
Annotating the Property
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "Response")
public class Response {
String signatureValue;
#XmlElement(name = "SignatureValue")
public String getSignatureValue() {
return signatureValue;
}
public void setSignatureValue(String signatureValue) {
this.signatureValue = signatureValue;
}
}
For More Information
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
Demo Code
Below is some demo code that reads/writes the XML corresponding to your Response class.
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Response.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum19713886/input.xml");
Response response = (Response) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(response, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<SignatureValue>Hello World</SignatureValue>
</Response>

"Class has two properties of the same name" error when converting an XML file into a Java object [duplicate]

This question already has answers here:
Jaxb, Class has two properties of the same name
(19 answers)
Closed 10 days ago.
I have an XML file like below, and I want to convert it into a Java object.
<P1>
<CTS>
Hello
</CTS>
<CTS>
World
</CTS>
<P1>
So I created the following Java classes with their properties.
P1 class
#XmlRootElement
public class P1 {
#XmlElement(name = "CTS")
List<CTS> cts;
}
CTS class
public class CTS {
String ct;
}
Test Class
File file = new File("D:\\ContentTemp.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(P1.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
P1 p = (P1) jaxbUnmarshaller.unmarshal(file);
But I am getting the following error:
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException:
2 counts of IllegalAnnotationExceptions
Class has two properties of the same name "cts"
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException:
2 counts of IllegalAnnotationExceptions
Class has two properties of the same name "cts"
By default a JAXB (JSR-222) implementation creates mappings based on properties and annotated fields. When you annotate a field for which there is also a property it will cause this error.
You have two options:
Use #XmlAccessorType(XmlAccessType.FIELD)
You could annotate the field you need to specify #XmlAccessorType(XmlAccessType.FIELD) on the class.
#XmlRootElement(name="P1)
#XmlAccessorType(XmlAccessType.FIELD)
public class P1 {
#XmlElement(name = "CTS")
List<CTS> cts;
}
Option #2 - Annotate the Property (get method)**
Alternatively you could annotate the get method.
#XmlRootElement(name="P1)
public class P1 {
List<CTS> cts;
#XmlElement(name = "CTS")
public List<CTS> getCts() {
return cts;
}
}
For More Information
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
FULL EXAMPLE
CTS
You can use the #XmlValue annotation to map to Java class to a complex type with simple content.
#XmlAccessorType(XmlAccessType.FIELD)
public class CTS {
#XmlValue
String ct;
}
P1
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="P1")
#XmlAccessorType(XmlAccessType.FIELD)
public class P1 {
#XmlElement(name = "CTS")
List<CTS> cts;
}
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(P1.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum13987708/input.xml");
P1 p1 = (P1) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(p1, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<P1>
<CTS>
Hello
</CTS>
<CTS>
World
</CTS>
</P1>
For More Information
http://blog.bdoughan.com/2011/06/jaxb-and-complex-types-with-simple.html
Two issues I can see:
1) You need to use P1.class in your JAXBContext. You haven't said what the Presentation class is, but if your root element is P1, that's what you need in the context:
JAXBContext jaxbContext = JAXBContext.newInstance(P1.class);
2) You need to specify the name of the root xml element:
#XmlRootElement(name="P1")
public class P1 {
...
Your XML looks like this:
<P1>
<CTS>
Hello
</CTS>
<CTS>
World
</CTS>
</P1>
But considering your mapping it should look like:
<p1>
<CTS>
<CT>
Hello
</CT>
</CTS>
<CTS>
<CT>
World
</CT>
</CTS>
</p1>
In order to change root element from p1 to P1 use attribute name from #XmlRootElement.
If you want to parse the first version of XML you posted, change your P1 class like this:
#XmlRootElement(name="P1")
public class P1 {
#XmlElement(name = "CTS")
List<String> cts;
}
You could try the following,
If you could, make xml as of the following structure.
<P1>
<CTSList>
<CTS value="Hello"/>
<CTS value="World"/>
</CTSList>
<P1>
And use,
#XMLRootElement(name="P1")
public class P1 {
List CTSList;
#XMLElementWrapper(name="CTSList")
#XMLELement(name="CTS")
public void setCTSList(List<CTS> ctsList) {
this.CTSList = ctsList;
}
public List<CTS> getCTSList() {
return this.CTSList;
}
}
#XMLRootElement(name="CTS")
public class CTS {
String cts;
#XMLAttribute(name = "value")
public String getCts() {
return this.cts;
}
public void set setCts(String cts) {
this.cts = cts;
}
}

Is it possible to programmatically configure JAXB?

Say I have two JavaBeans Person and Address.
If I create a list of Person objects, I'd like to marshal to something like this:
<persons>
<person>...</person>
</persons>
It's possible to use the technique described here:
Using JAXB to unmarshal/marshal a List<String>
By annotating JaxbList with #XmlRootElement(name = "persons") and #XmlElement(name = "person"), then it's possible to marshal to the XML above.
But, it'd be nice to be able to reuse the same JaxbList<T> class to also marshal a list of Address objects. And in reality, I will have many other types of beans. I can go with something like:
<list>
<item xsi:type="person" xmlns:xsi="http://www.w2.org/2001/XmlSchema-instance"></item>
</list>
But, ideally, it'd be nice to have it replace "list" with the plural version of class name and "item" with the class name.
So, is it possible to programmatically configure the JaxbContext or something during runtime and essentially set the value of the name inside #XmlRootElement and #XmlElement?
Or any other way to get this working without having to write a separate implementation of JaxbList for every bean type? Maybe XmlJavaTypeAdapter can achieve this sort of thing?
Update
#Blaise Doughan's solution accepted below works great. For my use case, I needed to go straight from Java object to XML, here's what worked (note this is not my full implementation, it's sort of just pseudo code for demonstration):
//JAXBContext is thread safe and so create it in constructor or
//setter or wherever:
...
JAXBContext jc = JAXBContext.newInstance(Wrapper.class, clazz);
...
public String marshal(List<T> things, Class clazz) {
//configure JAXB and marshaller
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
//Create wrapper based on generic list of objects
Wrapper<T> wrapper = new Wrapper<T>(things);
JAXBElement<Wrapper> wrapperJAXBElement = new JAXBElement<Wrapper>(new QName(clazz.getSimpleName().toLowerCase()+"s"), Wrapper.class, wrapper);
StringWriter result = new StringWriter();
//marshal!
m.marshal(wrapperJAXBElement, result);
return result.toString();
}
You could create a generic Wrapper object like the following:
Wrapper
You could create a generic wrapper class with a List property annotated with #XmlAnyElement(lax=true). The type of the object used to populate this list will be based on its root element (see: http://blog.bdoughan.com/2010/08/using-xmlanyelement-to-build-generic.html).
package forum13272288;
import java.util.*;
import javax.xml.bind.annotation.XmlAnyElement;
public class Wrapper<T> {
private List<T> items = new ArrayList<T>();
#XmlAnyElement(lax=true)
public List<T> getItems() {
return items;
}
}
Address
You will need to annotate the possible contents of the list with #XmlRootElement.
package forum13272288;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Address {
}
Person
package forum13272288;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Person {
}
Demo
The demo code below demonstrates how to use the Wrapper class. Since the root element can be different you will need to specify that you want to unmarshal to the wrapper class. Alternatively you could leverage the #XmlElementDecl annotation to associate multiple root elements with the wrapper class (see: http://blog.bdoughan.com/2012/07/jaxb-and-root-elements.html).
package forum13272288;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Wrapper.class, Person.class, Address.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StreamSource personsXML = new StreamSource("src/forum13272288/persons.xml");
JAXBElement<Wrapper> wrapper1 = unmarshaller.unmarshal(personsXML, Wrapper.class);
marshaller.marshal(wrapper1, System.out);
StreamSource addressesXML = new StreamSource("src/forum13272288/addresses.xml");
JAXBElement<Wrapper> wrapper2 = unmarshaller.unmarshal(addressesXML, Wrapper.class);
marshaller.marshal(wrapper2, System.out);
}
}
Output
Below is the output from running the demo code. The files persons.xml and addresses.xml look just like there corresponding output.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persons>
<person/>
<person/>
</persons>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addresses>
<address/>
<address/>
</addresses>
For More Information
http://blog.bdoughan.com/2012/11/creating-generic-list-wrapper-in-jaxb.html

Categories