How to annotate a list using #XmlElement? - java

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

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

JAXBException occurred : class com.jaxb.model.copy.copy.Snapshot nor any of its super class is known to this context..

Can someone help me to resolve the issue,
JAXBException occurred : class com.jaxb.model.copy.copy.Snapshot nor any of its super class is known to this context..
Interface JaxbWebResource
package com.jaxb.model.copy.copy;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
#Path("/jaxb")
public interface JaxbWebResource
{
#GET
#Produces({ "application/xml", "application/json" })
public Response readOffer(#Context HttpServletRequest req);
}
Class JaxbWebResourceImpl
package com.jaxb.model.copy.copy;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.xml.bind.JAXBElement;
import org.springframework.stereotype.Component;
import com.jaxb.model.copy.copy.Foo;
import com.jaxb.model.copy.copy.Moo;
import com.jaxb.model.copy.copy.ObjectFactory;
import com.jaxb.model.copy.copy.Snapshot;
#Component("jaxbWebResource")
public class JaxbWebResourceImpl implements JaxbWebResource
{
#Override
public Response readOffer(final HttpServletRequest req) {
System.out.println("JaxbWebResourceImpl readMarketingOffer");
Snapshot snapshot = new Snapshot();
Foo.Detail.Bar barV = new Foo.Detail.Bar();
barV.setBaz("heloo baz");
JAXBElement<Foo.Detail.Bar> bar = new ObjectFactory().createBar(barV);
bar.setNil(true);
Foo.Detail detail = new Foo.Detail();
detail.setBar(bar);
Foo foo = new Foo();
foo.setDetail(detail);
snapshot.setMoo(foo);
return Response.status(Status.OK).entity(snapshot).build();
}
}
ObjectFactory class
package com.jaxb.model.copy.copy;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
#XmlElementDecl(namespace="",name="bar")
public JAXBElement<Moo.Detail.Bar> createBar(Moo.Detail.Bar bar) {
return new JAXBElement<Moo.Detail.Bar>(new QName("bar"), Moo.Detail.Bar.class, bar);
}
}
Foo class
package com.jaxb.model.copy.copy;
import java.math.BigDecimal;
import javax.xml.bind.JAXBElement;
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.XmlElementRef;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
import com.guthyrenker.soma.ws.rest.v1.model.ObjectFactory;
import com.guthyrenker.soma.ws.rest.v1.model.WSMarketingOfferOP;
import com.guthyrenker.soma.ws.rest.v1.model.WSMarketingOfferWEB;
import com.jaxb.model.copy.Snapshot;
import com.jverstry.annotations.generics.Market;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo extends Moo{
#XmlElement
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Moo class
package com.jaxb.model.copy.copy;
import java.math.BigDecimal;
import javax.xml.bind.JAXBElement;
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.XmlElementRef;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
import com.jverstry.annotations.generics.Market;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Moo {
#XmlElement
protected Detail detail;
public Detail getDetail() {
return detail;
}
public void setDetail(Detail detail) {
this.detail = detail;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = { "bar"})
public static class Detail
{
#XmlElementRef(name="bar")
private JAXBElement<Foo.Detail.Bar> bar;
public JAXBElement<Foo.Detail.Bar> getBar() {
return bar;
}
public void setBar(JAXBElement<Foo.Detail.Bar> bar) {
this.bar = bar;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = { "value" })
public static class Bar
{
#XmlAttribute
protected String baz;
#XmlValue
protected BigDecimal value;
public String getBaz() {
return baz;
}
public void setBaz(String baz) {
this.baz = baz;
}
public BigDecimal getValue() {
return value;
}
public void setValue(BigDecimal value) {
this.value = value;
}
}
}
}
Snapshot class
package com.jaxb.model.copy.copy;
import java.util.Date;
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 javax.xml.bind.annotation.XmlType;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.guthyrenker.soma.ws.rest.v1.adapter.JsonDateTimeSerializer;
/**
* #author Srinivasa Kankipati
* #since 1.0
*/
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(propOrder = { "mask", "xmlns", "currentAsOf","moo" })
#XmlRootElement(name = "snapshot")
public class Snapshot<T extends Moo>
{
#XmlAttribute
public String mask;
#XmlElement
public Date currentAsOf;
#XmlAttribute
#JsonIgnore
public String xmlns;
#XmlElement(name = "moo")
private Moo moo;
public String getMask() {
return mask;
}
public void setMask(String mask) {
this.mask = mask;
}
public Date getCurrentAsOf() {
return currentAsOf;
}
public void setCurrentAsOf(Date currentAsOf) {
this.currentAsOf = currentAsOf;
}
public String getXmlns() {
return xmlns;
}
public void setXmlns(String xmlns) {
this.xmlns = xmlns;
}
public Moo getMoo() {
return moo;
}
public void setMoo(Moo moo) {
this.moo = moo;
}
}
JAX-RS will build a JAXBContext based on what the JAX-RS method returns. JAXB will then pull in more classes via transitive relationships. This doesn't always cause all the classes to be processed. To get more control over the JAXBContext created you can create a ContextResolver that builds the JAXBContext the way that you want.
import java.util.*;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
#Provider
#Produces(MediaType.APPLICATION_XML)
public class MyContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext jc;
public MyContextResolver() {
try {
jc = // Build Your JAXBContext on all classes it needs to be aware of.
} catch(JAXBException e) {
throw new RuntimeException(e);
}
}
public JAXBContext getContext(Class<?> clazz) {
// Return the JAXBContext for any class it is aware of otherwise return null.
}
}
I have provided some additional details on how your JAXBContext should be built in an answer to your related question:
Exception in thread "main" javax.xml.bind.JAXBException: class nor any of its super class is known to this context
Thanks to #blaise-doughan. I had a similar problem. My SOAP service worked fine but REST responses were lost because of what you have mentioned and I have merged his solution with my current Implementation:
#Provider
#Service("myManager")
#WebService(serviceName = "MyService", portName = "MyManagerImpl")
public class MyManagerImpl implements MyService, ContextResolver<JAXBContext> {
private JAXBContext jc;
public MyManagerImpl() {
try {
jc = JAXBContext.newInstance(Children.class, MyResponse.class);
} catch(JAXBException e) {
throw new RuntimeException(e);
}
public JAXBContext getContext(Class<?> aClass) {
return jc;
}
}
enter code here
Now everything work fine.

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