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;
}
}
Related
I'm new to spring boot and I'm trying to consume a Soap service from here
using spring boot somehow my code doesn't seem to work. I have generated classes from that wsdl which were saved in my generated-sources folder. How can I consume a remote wsdl properly?
Generated classes folder
Generated classes code
package net.webservicex;
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 javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"fromCurrency",
"toCurrency"
})
#XmlRootElement(name = "ConversionRate")
public class ConversionRate {
#XmlElement(name = "FromCurrency", required = true)
#XmlSchemaType(name = "string")
protected Currency fromCurrency;
#XmlElement(name = "ToCurrency", required = true)
#XmlSchemaType(name = "string")
protected Currency toCurrency;
public Currency getFromCurrency() {
return fromCurrency;
}
public void setFromCurrency(Currency value) {
this.fromCurrency = value;
}
public Currency getToCurrency() {
return toCurrency;
}
public void setToCurrency(Currency value) {
this.toCurrency = value;
}
}
ConversionRateResponse.java
package net.webservicex;
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 javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"conversionRateResult"
})
#XmlRootElement(name = "ConversionRateResponse")
public class ConversionRateResponse {
#XmlElement(name = "ConversionRateResult")
protected double conversionRateResult;
public double getConversionRateResult() {
return conversionRateResult;
}
public void setConversionRateResult(double value) {
this.conversionRateResult = value;
}
}
Country.java
package net.webservicex;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlType;
#XmlType(name = "Currency")
#XmlEnum
public enum Currency {
AFA,
ALL,
DZD,
ARS,
USD,
TRY;
/*Removed some country codes*/
public String value() {
return name();
}
public static Currency fromValue(String v) {
return valueOf(v);
}
}
ObjectFactory.java
package net.webservicex;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
private final static QName _Double_QNAME = new QName("http://www.webserviceX.NET/", "double");
public ObjectFactory() {
}
public ConversionRate createConversionRate() {
return new ConversionRate();
}
public ConversionRateResponse createConversionRateResponse() {
return new ConversionRateResponse();
}
#XmlElementDecl(namespace = "http://www.webserviceX.NET/", name = "double")
public JAXBElement<Double> createDouble(Double value) {
return new JAXBElement<Double>(_Double_QNAME, Double.class, null, value);
}
}
And I'm calling the service from here, somehow this doesn't work.
package com.wsdl;
import net.webservicex.ConversionRate;
import net.webservicex.ConversionRateResponse;
import net.webservicex.Currency;
import net.webservicex.ObjectFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Created by tenten on 2017/02/15.
*/
#RestController
public class Testing {
#RequestMapping("/ConversionRate")
public double conversionRate() {
ObjectFactory objectFactory = new ObjectFactory();
ConversionRate conversionRate = objectFactory.createConversionRate();
ConversionRateResponse conversionRateResponse = objectFactory.createConversionRateResponse();
Currency usd = Currency.USD;
usd.value();
Currency btn = Currency.BTN;
btn.value();
conversionRate.setToCurrency(usd);
conversionRate.setFromCurrency(btn);
conversionRateResponse.setConversionRateResult(12.00);
return conversionRateResponse.getConversionRateResult();
}
}
i am getting the following while reading from my .xml file.here is the error
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.dJohn.com/teacher", local:"teacher"). Expected elements are (none)
And here is my file.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Teacher xmlns="http://www.dJohn.com/teacher" Id="0001" />
main class
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
public class convertorDemo {
public static void main(String[] args) {
try {
File file = new File("C:\\file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(Teacher.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Teacher mrS= (Teacher ) jaxbUnmarshaller.unmarshal(file);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
and here is Teacher.java..leaving some other details
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Teacher", propOrder = {
})
public class Teacher{
#XmlAttribute(name = "Id")
protected String Id;
public String getId() {
return Id;
}
public void setId(String value) {
this.Id = value;
}
}
I don't know what my problem is and i am new to JAXB.please help!!
Annotate your Tana class with XmlRootElement.
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;
}
}
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.
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.