How to generate XML with variable value nodes - java

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>

Related

JAXB - customize idref value

I would like to customize idrefs values and prepend a "#", like this simple example, where the friends attribute contains idrefs to Animal or Person elements.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<Animal friends="#id1 #id2" id="id0"/>
<Animal friends="#id0" id="id1"/>
<Person friends="#id0" id="id2"/>
</root>
With Jaxb basic id/idref annotations I get this result (see my code below):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<Animal friends="id1 id2" id="id0"/>
<Animal friends="id0" id="id1"/>
<Person friends="id0" id="id2"/>
</root>
Is there a way to customize how the idref values are marshalled/unmarshalled?
My code
Test Class
package so;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Test {
public static void main (String [] args) {
Root r = new Root();
Animal foo = new Animal("id0");
Animal bar = new Animal("id1");
Person gaz = new Person("id2");
foo.addFriend(bar);
foo.addFriend(gaz);
bar.addFriend(foo);
gaz.addFriend(foo);
r.addMember(foo);
r.addMember(bar);
r.addMember(gaz);
try {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
marshaller.marshal(r, System.out);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Root.class
package so;
import java.util.ArrayList;
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.XmlElements;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlElements
({
#XmlElement(name = "Animal", type = Animal.class, required = false),
#XmlElement(name = "Person", type = Person.class, required = false)
})
private List<Friend> members;
public void addMember(Friend f) {
members.add(f);
}
public Root() {
members = new ArrayList<Friend>();
}
}
Person.class
package so;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlIDREF;
#XmlAccessorType(XmlAccessType.FIELD)
public class Person extends Friend {
#XmlAttribute
#XmlIDREF
private List<Friend> friends;
public Person(String id) {
super(id);
friends = new ArrayList<Friend>();
}
public void addFriend(Friend f) {
friends.add(f);
}
}
Animal class
package so;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlIDREF;
#XmlAccessorType(XmlAccessType.FIELD)
public class Person extends Friend {
#XmlAttribute
#XmlIDREF
private List<Friend> friends;
public Person(String id) {
super(id);
friends = new ArrayList<Friend>();
}
public void addFriend(Friend f) {
friends.add(f);
}
}
Friend class
package so;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlSeeAlso;
#XmlSeeAlso({Person.class, Animal.class})
#XmlAccessorType(XmlAccessType.FIELD)
public class Friend {
#XmlAttribute
#XmlID
private String id;
public Friend(String id) {
super();
this.id = id;
}
}

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

JAXB un-marshaling return null

I have a class with the following structure
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({
Child.class
})
public abstract class Parent implements Serializable{}
And
public class Child extends Parent implements Serializable{
private String attribute;
private List<String> values = new ArrayList<String>() ;
}
So while marshaling a child object it is saved successfully at the database as :
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Child >
<attribute>age</attribute>
<values>1</values>
<values>2</values>
</Child >
The problem is while un-marshaling that object , The unmarshal function return null.
JAXB.unmarshal(reader, Parent.class)
Could you please advise what is the problem , and how to solve it ?
Thanks in advance
When unmarshalling, you must:
Provide as root of your XML an element marked with #XMLRootElement.
Define your classes as fully compliant Java Beans (get/set methods over attributes).
Here is the code that worked for me:
Parent.java
package dummy;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
#XmlRootElement
#XmlSeeAlso({
Child.class
})
public abstract class Parent implements Serializable{}
Child (with a small main method marshalling and unmarshalling the same payload)
package dummy;
import java.io.ByteArrayInputStream;
import java.io.Serializable;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Child extends Parent implements Serializable
{
private String attribute;
private List<String> values = new ArrayList<String>();
/**
* #return the attribute
*/
public String getAttribute()
{
return attribute;
}
/**
* #param attribute
* the attribute to set
*/
public void setAttribute(String attribute)
{
this.attribute = attribute;
}
/**
* #return the values
*/
public List<String> getValues()
{
return values;
}
/**
* #param values
* the values to set
*/
public void setValues(List<String> values)
{
this.values = values;
}
public static void main(String[] args) throws JAXBException
{
JAXBContext context = JAXBContext.newInstance(Parent.class);
Child child = new Child();
child.setAttribute("dummy");
child.setValues(Arrays.asList("value1", "value2"));
StringWriter writer = new StringWriter();
context.createMarshaller().marshal(child, writer);
System.out.println(writer.getBuffer().toString());
Child unmarshalledChild = (Child) context.createUnmarshaller().unmarshal(new ByteArrayInputStream(writer.getBuffer().toString().getBytes()));
System.out.println("attribute: " + unmarshalledChild.attribute);
System.out.println("values: " + unmarshalledChild.values);
}
}
And the successful output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><child> <attribute>dummy</attribute><values>value1</values><values>value2</values></child>
attribute: dummy
values: [value1, value2]

java jaxb simple parsing is requiring #XmlAccessorType(XmlAccessType.FIELD) annotation

I am trying to parse an xml to java objects, I've read and implemented the following tutorial:
http://www.vogella.com/articles/JAXB/article.html (works perfectly)
But when I create my own clases (similar to those in the tutorial)
I get: Exception in thread "main" com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Class has two properties of the same name "clienteList"
Unless I use #XmlAccessorType(XmlAccessType.FIELD) on class Clientes but in the tutorial is not being used.
Any ideas ?
(It works fine with the #XmlAccessorType(XmlAccessType.FIELD) annotation but I want to know why is it being required with my classes while it is not for the classes in the tutorial)
Thank you in advance for any information.
Class Cliente
package mx.com.findep.crediseguros.dto.servicios.finsol;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "cliente")
public class Cliente {
private String numeroPersona;
#XmlElement(name = "numeroPersona")
public String getNumeroPersona() {
return numeroPersona;
}
public void setNumeroPersona(String numeroPersona) {
this.numeroPersona = numeroPersona;
}
}
Class Clientes
package mx.com.findep.crediseguros.dto.servicios.finsol;
import java.util.ArrayList;
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(name = "clientes")
//#XmlAccessorType(XmlAccessType.FIELD) //without this line it fails
public class Clientes {
// XmLElementWrapper generates a wrapper element around XML representation
#XmlElementWrapper(name = "clienteList")
// XmlElement sets the name of the entities
#XmlElement(name = "cliente")
private ArrayList<Cliente> clienteList;
public void setClienteList(ArrayList<Cliente> clienteList) {
this.clienteList = clienteList;
}
public ArrayList<Cliente> getClienteList() {
return clienteList;
}
}
Testing My Marshalling
package mx.com.findep.crediseguros.dto.servicios.finsol;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class TestClientesXml {
private static final String SOME_XML = "C:/bea/user_projects/domains/DominioDesarrollo/esquemas/calculoCostoSeguroPeticion.xml";
public static void main(String[] args) throws JAXBException, IOException {
ArrayList<Cliente> clienteList = new ArrayList<Cliente>();
Cliente cliente1 = new Cliente();
cliente1.setNumeroPersona("1");
clienteList.add(cliente1);
Cliente cliente2 = new Cliente();
cliente2.setNumeroPersona("2");
clienteList.add(cliente2);
Clientes clientes = new Clientes();
clientes.setClienteList(clienteList);
JAXBContext context = JAXBContext.newInstance(Clientes.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(clientes, System.out);
m.marshal(clientes, new File(SOME_XML));
System.out.println();
System.out.println("Output from our XML File: ");
Unmarshaller um = context.createUnmarshaller();
Clientes clientes2 = (Clientes) um.unmarshal(new FileReader(SOME_XML));
ArrayList<Cliente> list = clientes2.getClienteList();
for (Cliente cliente : list) {
System.out.println("Cliente: " + cliente.getNumeroPersona());
}
}
}
By default JAXB treats public fields and properties as mapped. If you annotate a field it then considers the field and property as mapped causing the conflict. Without #XmlAccessorType(XmlAccessType.FIELD) you should annotate the get or set method.
For More Information
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
So lets say we have:
#XmlRootElement(name = "book")
public class Book {
#XmlElement(name = "book_title")
private String title;
public getTitle(){..}
public setTitle(){...}
}
if we run the code we will have
Exception in thread "main"
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Class has two properties of the same name "title"
this problem is related to the following location:
at public java.lang.String com.example.jaxb.Book.getTitle()
at com.example.jaxb.Book
this problem is related to the following location:
at private java.lang.String com.example.jaxb.Book.title
at com.example.jaxb.Book
But if we add the annotation: XmlAccessorType
#XmlRootElement(name = "book")
#XmlAccessorType(XmlAccessType.FIELD)
public class Book {
the error will disappear.
When have class which want to marshall and have 10 fields, I prefer to annotate only fields, not one time the setter then the getter. So use #XmlAccessorType and annotate only the fields.

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.

Categories