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.
Related
I'm trying to convert a Java bean into an xml document and I'm having trouble with some of these more complex interfaces. Here is the setup:
protected Set<Object> field1;
protected Map<Integer, List<Object>> field2;
protected List<String> field3;
protected List<Object> field4;
protected List<Object> field5;
protected List<Object> field6;
protected List<String> field7;
protected List<Object> field8;
In each Object (which is itself a bean) I have the following at the top of each class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"field1",
"field2",
"field3",
"field4",
"field5",
"field6",
"field7",
"field8"
})
#XmlRootElement(name = "root")
I keep getting an exception for the Map of Integers and Lists when I marshal the whole bean. Is there something that I'm missing?
Suppose you have below three class
Customer class
package comparison;
import java.util.ArrayList;
import java.util.List;
public class Customer {
private long id;
private String name;
private Address address;
private List<phonenumber> phoneNumbers;
public Customer() {
phoneNumbers = new ArrayList<PhoneNumber>();
}
}
Address class
package comparison;
public class Address {
private String city;
private String street;
}
and PhoneNumber class
package comparison;
public class PhoneNumber {
private String type;
private String number;
}
Now adding some dummy data
package comparison;
public class Data {
public static Customer CUSTOMER;
static {
CUSTOMER = new Customer();
CUSTOMER.setId(123);
CUSTOMER.setName("Jane Doe");
Address address = new Address();
address.setStreet("1 A Street");
address.setCity("Any Town");
CUSTOMER.setAddress(address);
PhoneNumber workPhoneNumber = new PhoneNumber();
workPhoneNumber.setType("work");
workPhoneNumber.setNumber("555-WORK");
CUSTOMER.getPhoneNumbers().add(workPhoneNumber);
PhoneNumber cellPhoneNumber = new PhoneNumber();
cellPhoneNumber.setType("cell");
cellPhoneNumber.setNumber("555-CELL");
CUSTOMER.getPhoneNumbers().add(cellPhoneNumber);
}
}
So now you apply marshalling to convert the object to xml
package comparison.jaxb;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.namespace.QName;
import comparison.Customer;
import static comparison.Data.CUSTOMER;
public class JAXBDemo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Customer.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
JAXBElement<Customer> jaxbElement = new JAXBElement<Customer>(new QName("customer"), Customer.class, CUSTOMER);
marshaller.marshal(jaxbElement, System.out);
}
}
A JAXBContext needs to be initialized on the binding metadata before
the marshal operation can occur.
Unlike XStream JAXB does not format
the XML by default, so we will enable this feature.
With no metadata
specified we need to supply JAXB with a root element name (and
namespace).
The code will produce result:
<customer>
<id>123</id>
<address>
<city>Any Town</city>
<street>1 A Street</street>
</address>
<name>Jane Doe</name>
<phoneNumbers>
<number>555-WORK</number>
<type>work</type>
</phoneNumbers>
<phoneNumbers>
<number>555-CELL</number>
<type>cell</type>
</phoneNumbers>
By default JAXB will access public fields and properties. You can configure JAXB to use field access with the following package level annotation.
#XmlAccessorType(XmlAccessType.FIELD)
package comparison;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
You can look at this blogpost for details.
I have a jsp file with some xml data inside which is linked to an xslt stylesheet.I have several classes, user, users, testJAXB, and DiaryApplication.
My question is how do I call user method or class that will allow me to instead of typing out strings in between the xml tags like joe#average.com I want to be able to do something like this
<% user.getEmail(); %> meaning I can use scriplets to invoke data instead of typing it. How do I do this.
//////////////Below is my JSP file.
<%#page contentType="Application/xml"%><?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="main.xsl"?>
<% String filePath = application.getRealPath("WEB-INF/users.xml"); %>
<jsp:useBean id="diaryApp" class="anypackage.DiaryApplication" scope="application">
<jsp:setProperty name="diaryApp" property="filePath" value="<%=filePath%>"/>
</jsp:useBean>
<% ??? ???? ???%>
<users>
<user>
<email>user</email>
<username>average user</username>
<password>blahblah</password>
</user>
<user>
<email>joe#bloggs.com</email>
<username>Joe Bloggs</username>
<password>foobar</password>
</user>
<user>
<email>Average#joehotmail.com</email>
<username>joe average</username>
<password>password</password>
</user>
<user>
<email>user#email.com</email>
<username>user</username>
<password>password</password>
</user>
</users>
/////// Below is two Classes user and Users
package anypackage;
import java.util.*;
import java.io.Serializable;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "users")
public class Users implements Serializable {
// The list of user elements does NOT have an extra wrapper element.
// See the comment in the XML file, and compare to the bookshop example.
#XmlElement(name = "user")
private ArrayList<User> list = new ArrayList<User>();
public ArrayList<User> getList() {
return list;
}
public void addUser(User user) {
list.add(user);
}
public void removeUser(User user) {
list.remove(user);
}
public User login(String email, String password) {
// For each user in the list...
for (User user : list) {
if (user.getEmail().equals(email) && user.getPassword().equals(password))
return user; // Login correct. Return this user.
}
return null; // Login incorrect. Return null.
}
}
package anypackage;
import java.io.Serializable;
import java.util.*;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlAccessorType(XmlAccessType.FIELD)
public class User implements Serializable{
#XmlElement(name = "email")
private String email;
#XmlElement(name = "username")
private String username;
#XmlElement(name = "password")
private String password;
public User() {
super();
// TODO Auto-generated constructor stub
}
public User(String email, String username, String password) {
this.email = email;
this.username = username;
this.password = password;
}
public String getEmail() {
return email;
}
public void setName(String email) {
this.email = email;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
////// Below is my DiaryApplication class
package anypackage;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
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.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
#XmlAccessorType(XmlAccessType.PROPERTY)
public class DiaryApplication {
private String filePath;
private Users users;
public DiaryApplication(String filePath, Users users) {
super();
this.filePath = filePath;
this.users = users;
}
public DiaryApplication() {
super();
// TODO Auto-generated constructor stub
}
#XmlElement
public String getFilePath() {
return filePath;
}
#XmlElement
public void setFilePath(String filePath) throws JAXBException, IOException {
this.filePath = filePath;
// This is the file path given to us.
// We should use it
// Load the users from the XML file...
JAXBContext jc = JAXBContext.newInstance(Users.class);
Unmarshaller u = jc.createUnmarshaller();
FileInputStream fin = new FileInputStream(filePath); // use the given file path
users = (Users)u.unmarshal(fin); // This loads the "users" object
fin.close();
}
#XmlElement
public Users getUsers() {
return users;
}
public void setUsers(Users users) {
this.users = users;
}
#XmlElement
public void saveUsers() throws JAXBException, IOException {
JAXBContext jc = JAXBContext.newInstance(Users.class);
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
FileOutputStream fout = new FileOutputStream(filePath);
m.marshal(users, fout);
fout.close();
}
}
/////////////////Below is my TestJAXB class
package anypackage;
import java.util.*;
import java.io.*;
import javax.xml.bind.*;
public class TestJAXB implements Serializable {
public static void main(String[] args) throws Exception {
Users users = new Users();
users.addUser(new User("randomegue#askdm.com", "tervor", "blahblah", "male", "green"));
users.addUser(new User("joe#bloggs.com", "Joe Bloggs", "foobar", "male", "yellow"));
// Boilerplate code to convert objects to XML...
JAXBContext jc = JAXBContext.newInstance(Users.class);
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.marshal(users, System.out);
}
}
In a well-designed MVC application, JSP shouldn't call business logic (Java classes), but should just receive values to display.
In other words, it should be aware just of the logic necessary to present received data.
By the way, if you want to proceed in this way you can do it.
To use a Java method in a JSP file you have to:
import your class ad the beginning of the file, e.g. <%# page import="full.package.path.ClassName" %>
then you can directly use its static method in your scriptlet, e.g. <% ClassName.staticMethod(params...) %>
if method you need isn't static you need to construct an instance, then to use the method, e.g. <% ClassName c = new ClassName(); c.staticMethod(params...) %>
Hope it helps
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>
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;
}
}
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.