Get object reference name in Java for constructing XML - java

I am writing custom XML marshaller using XML generators. I am stuck with tag names writing.
Example:
List<String> userList = new ArrayList<String>();
userList.add("UserA");
userList.add("UserB");
Map<String, Object> systemMap = new HashMap<String, Object>();
systemMap.put("SystemA",userList);
My requirement is:
<SystemA>
<userList>
[userA,userB]
</userList>
</SystemA>

You can achieve that using Jaxb. Please extract inner classes to file before implementing.
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.io.StringWriter;
import java.util.*;
public class ParsingTest {
private JAXBContext jaxbContext;
private Marshaller marshaller;
#Before
public void setUp() throws Exception {
jaxbContext = JAXBContext.newInstance(SystemA.class);
marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
}
#Test
public void testParse() throws Exception {
final SystemA systemA = new SystemA();
final List<String> users = new ArrayList<>();
users.add("userA");
users.add("userB");
systemA.setUserList(users);
final StringWriter stringWriter = new StringWriter();
marshaller.marshal(systemA, stringWriter);
System.out.println(stringWriter.toString());
Assert.assertTrue(true);
// XMLUnit.compareXML(stringWriter.toString(), reader);
}
#XmlRootElement(name = "SystemA")
#XmlType(name = "SystemA", propOrder = {
"userList"
})
#XmlAccessorType(XmlAccessType.FIELD)
public static class SystemA {
#XmlJavaTypeAdapter(CustomAdapter.class)
private Collection<String> userList;
public Collection<String> getUserList() {
return userList;
}
public void setUserList(Collection<String> userList) {
this.userList = userList;
}
}
public static class CustomAdapter extends XmlAdapter<String, Collection<String>> {
#Override
public Collection<String> unmarshal(String v) throws Exception {
return new ArrayList<>();// TODO String -> List;
}
#Override
public String marshal(Collection<String> v) throws Exception {
final StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("[");
for (String s : v) {
stringBuilder.append(s);
stringBuilder.append(",");
}
stringBuilder.deleteCharAt(stringBuilder.lastIndexOf(","));
stringBuilder.append("]");
return stringBuilder.toString();
}
}
}
This will output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SystemA>
<userList>[userA,userB]</userList>
</SystemA>

Related

How to generate XML with variable value nodes

I need to generate XML using jaxb where we have variable value nodes. We can have 3 values or 5 values or even more like
<custom-attribute>
<value>Green</value>
<value>Red</value>
</custom-attribute>
In the pojo we can use List like bellow
class CustomAttribute() {
#XmlValue
#XmlList
public List<String> value
}
but that adds values with space separated string like bellow
<custom-attribute>Green Red</custom-attribute>
How can I generate desired XML with multiple value nodes?
I provide below the code, you can try to run.
First you have to create a class called Value as given below.
import javax.xml.bind.annotation.XmlValue;
public class Value {
private String data;
#XmlValue
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}
Then you have to create a class called CustomAttribute like this.
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.ArrayList;
import java.util.List;
#XmlRootElement(name = "custom-attribute")
#XmlAccessorType(XmlAccessType.PROPERTY)
class CustomAttribute {
public List<Value> value;
public List<Value> getValue() {
return value;
}
public void setValue(List<Value> values) {
this.value = values;
}
}
I provide below the Test class to check.
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import java.util.ArrayList;
import java.util.List;
public class TestCustomAttribute {
public static void main(String[] args) throws Exception {
List<Value> valueList = new ArrayList<>();
Value value1 = new Value();
value1.setData("Green");
valueList.add(value1);
Value value2 = new Value();
value2.setData("Red");
valueList.add(value2);
CustomAttribute ca = new CustomAttribute();
ca.setValue(valueList);
JAXBContext jaxbContext = JAXBContext.newInstance(CustomAttribute.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(ca, System.out);
}
}
The formed XML will be like this.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<custom-attribute>
<value>Green</value>
<value>Red</value>
</custom-attribute>

Unmarshalling Complex Nested XML file using XMLStreamReader without XSD

I am trying to unmarshal nested XML file using XMLStreamReader. My XML file looks like this :
<?xml version="1.0" encoding="UTF-8"?>
<tns:Envelope
xmlns:tns="http://www.w3.org/2003/05/soap-envelope-dial"
xmlns:lmic="http://www.example.com"
xmlns:producer="http://example1.com/"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:ns5="http://www.example.com/dial/3/0">
<tns:header>
...
...
</tns:header>
<tns:body>
<producer:Producer id="1234">
<producer:GenParty>
<producer:NameInfo>
<producer:Comm>
<producer:SuppName>DATA</producer:SuppName>
<producer:ContractNumber>123456</producer:ContractNumber>
</producer:Comm>
</producer:NameInfo>
<producer:Address>
<Street>ABC</Street>
<Country>DEF</Country>
...
...
</prodcer:Address>
<producer:Address>
<Street>ABC</Street>
<Country>DEF</Country>
...
...
</prodcer:Address>
</producer:GenParty>
</producer:Producer>
</tns:body>
</tns:emvelope>
I have created classes like the following:
#XmlRootElement(name="Producer",namespace="http://example.com/")
#XmlAccessorType(XmlAccessType.FIELD)
Class Producer {
private GenParty;
// getter method of class GenParty
// setter method of class GenParty
}
#XmlRootElement(name="GenParty")
#XmlAccessorType(XmlAccessType.FIELD)
class GenParty {
private NameInfo;
private List<Address> address;
//getter of both fields
// setter of both fields
}
and subsequent classes are defined.
I am using XMLStreamReader to advance to the tag and then I am writing my unmarshaler code as:
JAXBContext jc = JAXBContext.newInstance(Producer.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Producer producer = unmarshaller.unmarshal(xsr,Producer.class).getValue();
However, I am getting the null value set to on Producer object. Is there anything I am doing wrong? I could unmarshal simple XML files but this level of nesting is creating problems for me. Can someone please suggest easy of doing it or any changes I should make in my code skeleton?
Thanks a lot in advance!
It is a bit hard to say what you are doing wrong. Yet I would suggest to create a Producer in code and then marshall and unmarshall it to check, if all your classes are ok.
If the classes are ok and marshalling / unmarshalling works the producer variable should never be null.
Here is an example of how this exercise would look like:
import com.sun.xml.internal.ws.streaming.DOMStreamReader;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.util.List;
public class JAXBTester {
public static void main(String[] args) throws JAXBException, ParserConfigurationException, IOException, SAXException {
JAXBContext jc = JAXBContext.newInstance(Producer.class);
Marshaller marshaller = jc.createMarshaller();
Producer producer = createProducer();
String producerStr = marshalproducer(marshaller, producer);
Unmarshaller unmarshaller = jc.createUnmarshaller();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new ByteArrayInputStream(producerStr.getBytes("UTF-8")));
XMLStreamReader xmlStreamReader = new DOMStreamReader(document);
Producer readProducer = unmarshaller.unmarshal(xmlStreamReader, Producer.class).getValue();
if (readProducer == null) {
throw new IllegalStateException();
}
}
private static String marshalproducer(Marshaller marshaller, Producer producer) throws JAXBException {
StringWriter writer = new StringWriter();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(producer, writer);
String res = writer.toString();
System.out.println(res);
return res;
}
private static Producer createProducer() {
Producer producer = new Producer();
GenParty genParty = new GenParty();
producer.setGenParty(genParty);
NameInfo nameInfo = new NameInfo();
nameInfo.setInfo("Foo");
genParty.setNameInfo(nameInfo);
return producer;
}
}
#XmlRootElement(name = "Producer", namespace = "http://example.com/")
#XmlAccessorType(XmlAccessType.FIELD)
class Producer {
private GenParty genParty;
public GenParty getGenParty() {
return genParty;
}
public void setGenParty(GenParty genParty) {
this.genParty = genParty;
}
}
#XmlRootElement(name = "GenParty")
#XmlAccessorType(XmlAccessType.FIELD)
class GenParty {
private NameInfo nameInfo;
private List<Address> address;
public NameInfo getNameInfo() {
return nameInfo;
}
public void setNameInfo(NameInfo nameInfo) {
this.nameInfo = nameInfo;
}
public List<Address> getAddress() {
return address;
}
public void setAddress(List<Address> address) {
this.address = address;
}
}
class NameInfo {
private String info;
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
class Address {
private String street;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
If you execute this code with Java 8 it does not throw any exception.

How to annotate a list using #XmlElement?

I have the following annotation using javax.xml.bind.annotation.XmlElement:
#XmlElement
public List<String> getKeywords() {
return keywords;
}
Which produces the following XML when I marshall some example content:
<keywords>keyword1</keywords>
<keywords>keyword2</keywords>
I would like to get the following XML:
<keywords>
<keyword>keyword1</keyword>
<keyword>keyword2</keyword>
</keywords>
What kind of an annotation should I use?
I've tried
#XmlElementWrapper
#XmlElement(name="keyword")
But then the whole content disappears and the result is:
<keywords/>
The same happens also if I only try to rename the element:
#XmlElement(name="keyword")
What am I doing wrong?
UPDATE:
Here is the updated full code for the class according to the first answers, but it is still not working (the result is an empty list <keywords/> when marshalled to XML):
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Content {
private List<String> keywords;
public Content() {}
#XmlElementWrapper(name="keywords")
#XmlElement(name="keyword")
public List<String> getKeywords() {
return keywords;
}
public void setKeywords(List<String> keywords) {
this.keywords = keywords;
}
}
I also tried the following with the same result:
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Content {
#XmlElementWrapper(name="keywords")
#XmlElement(name="keyword")
private List<String> keywords;
public Content() {}
public List<String> getKeywords() {
return keywords;
}
public void setKeywords(List<String> keywords) {
this.keywords = keywords;
}
}
However, the keywords are not empty as the following produces <keywords>keyword1</keywords><keywords>keyword2</keywords> instead of an empty list:
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Content {
private List<String> keywords;
public Content() {}
#XmlElement
public List<String> getKeywords() {
return keywords;
}
public void setKeywords(List<String> keywords) {
this.keywords = keywords;
}
}
The code for marshalling is (JAX-RS):
import java.io.StringWriter;
import javax.ws.rs.Consumes;
import javax.ws.rs.Path;
import javax.ws.rs.POST;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
#Path("process")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_XML)
public class ContentHandler {
#POST
public Response process(Content content) {
StringWriter stringWriter = new StringWriter();
try {
JAXBContext jaxbContext = JAXBContext.newInstance(Content.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(content, stringWriter);
} catch (JAXBException e) {
return Response.serverError().entity(e.getMessage()).build();
}
return Response.ok(stringWriter.toString(), MediaType.APPLICATION_XML).build();
}
}
You need to leverage #XmlElementWrapper and #XmlElement.
Java Model
Content
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Content {
private List<String> keywords;
public Content() {}
#XmlElementWrapper
#XmlElement(name="keyword")
public List<String> getKeywords() {
return keywords;
}
public void setKeywords(List<String> keywords) {
this.keywords = keywords;
}
}
Demo Code
Demo
import java.util.*;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Content.class);
List<String> strings = new ArrayList<String>(2);
strings.add("foo");
strings.add("bar");
Content content = new Content();
content.setKeywords(strings);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(content, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<content>
<keywords>
<keyword>foo</keyword>
<keyword>bar</keyword>
</keywords>
</content>
For More Information
Below are links to a couple articles from my blog that provide additional information:
http://blog.bdoughan.com/2010/09/jaxb-collection-properties.html
http://blog.bdoughan.com/2012/12/jaxb-representing-null-and-empty.html
Use this form:
#XmlElementWrapper(name="keywords")
#XmlElement(name="keyword")
Please note that if keywords is empty then you will get <keywords />.
Sometimes you will need to add #XmlRootElement to your class (depends on the context) and the #XmlAccessorType(XmlAccessType.?) annotation. I usually use #XmlAccessorType(XmlAccessType.FIELD) and annotate my fields with #XmlElement.
Above answer by - Blaise Doughan is completely correct
Another simple way is , even if you don't write the - #XmlElementWrapper
private List<String> keywords;
#XmlElementWrapper
#XmlElement(name="keyword")
public List<String> getKeywords() {
return keywords;
}
You can use it this way - write the XmlAccessorType on Class level , then XML element name will be same as the class member name - keywords
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Content {
private List<String> keywords;
public Content() {}
public List<String> getKeywords() {
return keywords;
}
public void setKeywords(List<String> keywords) {
this.keywords = keywords;
}
}

Using generic #XmlJavaTypeAdapter to unmarshal wrapped in Guava's Optional

I'm trying to unmarshal some xml into java objects wrapped in Guava's Optional using a generic XmlJavaTypeAdapter. However, I can't get it to work properly using generics.
I'm using eclipselink 2.5.1 / moxy
XML:
<?xml version="1.0" encoding="UTF-8"?>
<page>
<label>Test</label>
<description>Test</description>
</page>
Page.java:
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.eclipse.persistence.oxm.annotations.XmlPath;
import com.google.common.base.Optional;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement()
public class Page {
#XmlPath("label")
private Label label;
#XmlJavaTypeAdapter(OptionalLabelAdapter.class)
private Optional<Label> description = Optional.absent();
}
Label.java:
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlValue;
#XmlAccessorType(XmlAccessType.FIELD)
public class Label {
#XmlValue
private String text;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
MoxyTest.java:
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class MoxyTest {
public static void main(String[] args) throws JAXBException {
String name = "test";
JAXBContext jc = JAXBContext.newInstance(Page.class);
System.out.println(jc.getClass());
Unmarshaller unmarshaller = jc.createUnmarshaller();
Page page = (Page) unmarshaller.unmarshal(new File("xml/" + name + ".xml"));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(page, System.out);
}
}
This is my adapter using generics:
import javax.xml.bind.annotation.adapters.XmlAdapter;
import com.google.common.base.Optional;
public class OptionalAdapter<T> extends XmlAdapter<T, Optional<T>> {
#Override
public Optional<T> unmarshal(T value) throws Exception {
return Optional.of(value);
}
#Override
public T marshal(final Optional<T> value) throws Exception {
if(value.isPresent()){
return value.get();
} else {
return null;
}
}
}
This doesn't work, I get a ElementNSImpl wrapped in an Optional. It does work if I use:
#XmlJavaTypeAdapter(OptionalAdapter.class)
#XmlElement(name = "description", type = Label.class)
private Optional<Label> description;
in Page.java. However, I'm not sure how to achieve the same with attributes.
Using this adapter does work:
import javax.xml.bind.annotation.adapters.XmlAdapter;
import com.google.common.base.Optional;
public class OptionalLabelAdapter extends XmlAdapter<Label, Optional<Label>> {
#Override
public Optional<Label> unmarshal(final Label value) throws Exception {
return Optional.of(value);
}
#Override
public Label marshal(final Optional<Label> value) throws Exception {
if(value.isPresent()){
return value.get();
} else {
return null;
}
}
}
Please explain why my generic adapter doesn't work without "#XmlElement(name = "description", type = Label.class)" and please explain how I achieve the same for attributes instead of elements.

serialize graph of javabeans into xml having separate xml file for each java instance

Could you please suggest a framework or tool which could serialize graph of javabeans into xml having separate xml file for each java instance? All the java xml tools i managed to find serialzie to single file, but i need them to be separate, for example:
Model:
class A {
B b;
}
class B {
}
A a = new A();
a.b = new B();
serialize to :
a.xml:
<a>
<property name="b>somehow ref to b</property>
</a>
b.xml
<b>
</b>
Best regards, ebu.
You could use JAXB and an XmlAdapter to do something like the following:
A
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
#XmlJavaTypeAdapter(MyAdapter.class)
public class A {
private B b;
private List<C> c;
public A() {
c = new ArrayList<C>();
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
public List<C> getC() {
return c;
}
public void setC(List<C> c) {
this.c = c;
}
}
B
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
#XmlJavaTypeAdapter(MyAdapter.class)
public class B {
}
C
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
#XmlJavaTypeAdapter(MyAdapter.class)
public class C {
}
MyAdapter
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class MyAdapter extends XmlAdapter<String, Object> {
private static int counter = 1;
private static JAXBContext jaxbContext;
static {
try {
jaxbContext = JAXBContext.newInstance(A.class, B.class, C.class);
} catch(JAXBException e) {
throw new RuntimeException(e);
}
}
#Override
public Object unmarshal(String v) throws Exception {
File xml = new File(v);
return jaxbContext.createUnmarshaller().unmarshal(xml);
}
#Override
public String marshal(Object v) throws Exception {
String filename = counter++ + ".xml";
File xml = new File(filename);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(v, xml);
return filename;
}
}
Demo
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(A.class, B.class, C.class);
A a = new A();
a.setB(new B());
a.getC().add(new C());
a.getC().add(new C());
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(a, System.out);
}
}

Categories