I have a simple class that looks like this
import nl.hu.pf.coproco.model.Observeable;
#XmlRootElement (name = "Purpose")
#XmlAccessorType (XmlAccessType.FIELD)
public class Purpose implements Observeable{
private String name;
private ArrayList<Observer> observers;
#XmlElement (name = "subPurpose")
private ArrayList<Purpose> subPurposes;
//methods
}
But Observable is an interface so I get an exception because JAXB can'ty handle interfaces. I tried figuring out how to ignore the field with the ArrayList<Observer> so it wont be exported to xml.
I tried using the #XmlTransient annotation but the I get the following eception javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"purpose"). Expected elements are <{}Purpose>
This happens when Unmarshalling but I want to Marshall that class too
Sample xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<purpose>
<name>All Purposes</name>
<subPurpose>
<name>Creational</name>
</subPurpose>
<subPurpose>
<name>Structural</name>
</subPurpose>
<subPurpose>
<name>Behavioral</name>
<subPurpose>
<name>Behavioral 1</name>
</subPurpose>
<subPurpose>
<name>Behavioral 2</name>
</subPurpose>
</subPurpose>
</purpose>
The interface Observable looks like this:
public interface Observeable {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers(Object o, String arg);
}
Your xml contains purpose not Purpose so change #XmlRootElement(name = "Purpose") to
#XmlRootElement(name = "purpose")
And add #XmlTransient on observers like this
#XmlTransient
private ArrayList<Observer> observers;
Related
In my spring boot application, I have below DTO class
#Data
public clsss Feed {
private int id;
private String name;
private String title;
#Builder
#XmlRootElement(name = "feeds")
public static class Feeds {
#XmlElement(name = "feed")
#Singular
private List<Feed> feeds;
}
}
My config class as below
#Component
public class JacksonCustomizer implements Jackson2ObjectMapperBuilderCustomizer {
#Override
public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
jacksonObjectMapperBuilder.modulesToInstall(new JaxbAnnotationModule());
}
}
DAO class implementation as below
public Feeds getAll() {
String sqlQuery = "SELECT * FROM feed WHERE trash = 0";
return Feeds.builder().feeds(namedParameterJdbcTemplate.query(sqlQuery, new BeanPropertyRowMapper<>(Feed.class))).build();
}
Using my ReST API, XML response I am receiving as below:
<feeds>
<feed>
<feed>
<id>1</id>
<name>Val1</name>
<title>Title1</title>
</feed>
<feed>
<id>2</id>
<name>Val2</name>
<title>Title2</title>
</feed>
</feed>
</feeds>
I want to remove <feed> which comes as a wrapper element. Desired output is as below:
<feeds>
<feed>
<id>1</id>
<name>Val1</name>
<title>Title1</title>
</feed>
<feed>
<id>2</id>
<name>Val2</name>
<title>Title2</title>
</feed>
</feeds>
Make changes in the config class to set the default wrapper to false.
#Component
public class JacksonCustomizer implements Jackson2ObjectMapperBuilderCustomizer {
#Override
public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
jacksonObjectMapperBuilder.modulesToInstall(new JaxbAnnotationModule());
jacksonObjectMapperBuilder.defaultUseWrapper(false); //This was missing before
}
}
I have the following class which shall be serialized/deserialized to XML.
#XmlRootElement(name = "nnxml")
#XmlAccessorType(XmlAccessType.FIELD)
public class InfoRequest {
#XmlElement(name = "vendor_id")
private String vendorId;
#XmlElement(name = "vendor_authcode")
private String authCode;
}
This currently gives me this XML which is consistent and correct:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<nnxml>
<vendor_id>vendor id</vendor_id>
<vendor_authcode>auth code</vendor_authcode>
</nnxml>
However I need to wrap the XML elements in another element like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<nnxml>
<info_request>
<vendor_id>vendor id</vendor_id>
<vendor_authcode>auth code</vendor_authcode>
</info_request>
</nnxml>
How can I wrap the above fields in a info_request element? Do I have to create something like an inner class or is there a simpler approach?
One approach is to create a Wrapper class like this and to insert your class
#XmlRootElement(name = "nnxml")
#XmlAccessorType(XmlAccessType.FIELD)
public class Nnxml implements Serializable {
#XmlElement(name = "info_request")
private InfoRequest request;
}
The annotations of subclass are optional
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "info_request", propOrder = {
"vendorId",
"authCode"
})
class InfoRequest implements Serializable{
#XmlElement(name = "vendor_id")
private String vendorId;
#XmlElement(name = "vendor_authcode")
private String authCode;
}
Output is
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<nnxml>
<info_request>
<vendor_id>vendor id</vendor_id>
<vendor_authcode>auth code</vendor_authcode>
</info_request>
</nnxml>
I've a service that returns this XML:
<?xml version="1.0" encoding="UTF-8"?>
<response>
<status>success</status>
<result>
<project>
<id>id1</id>
<owner>owner1</owner>
</project>
<project>
<id>id2</id>
<owner>owner2</owner>
</project>
</result>
or
<?xml version="1.0" encoding="UTF-8"?>
<response>
<status>success</status>
<result>
<user>
<id>id1</id>
<name>name1</name>
</user>
<user>
<id>id2</id>
<name>name2</name>
</user>
</result>
I want to unmarshall the retrieved XML using these classes:
Result:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Response<T> {
#XmlElement
protected String status;
#XmlElementWrapper(name = "result")
#XmlElement
protected List<T> result;
}
Project:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Project {
#XmlElement
public String id;
#XmlElement
public String owner;
}
User:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class User {
#XmlElement
public String id;
#XmlElement
public String name;
}
First not working solution
JAXBContext context = JAXBContext.newInstance(Response.class, Project.class, User.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
StreamSource source = new StreamSource(new File("responseProject.xml"));
Response<Project> responseProject = (Response<Project>)unmarshaller.unmarshal(source);
System.out.println(responseProject.getStatus());
for (Project project:responseProject.getResult()) System.out.println(project);
source = new StreamSource(new File("responseUser.xml"));
Response<User> responseUser = (Response<User>)unmarshaller.unmarshal(source);
System.out.println(responseUser.getStatus());
for (User user:responseUser.getResult()) System.out.println(user);
I get an empty list.
Second not working solution
Inspired by this article http://blog.bdoughan.com/2012/11/creating-generic-list-wrapper-in-jaxb.html I've modified the Response class:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Response<T> {
#XmlElement
protected String status;
#XmlAnyElement(lax=true)
protected List<T> result;
}
And then tested it with this code:
Response<Project> responseProject = unmarshal(unmarshaller, Project.class, "responseProject.xml");
System.out.println(responseProject.getStatus());
for (Project project:responseProject.getResult()) System.out.println(project);
private static <T> Response<T> unmarshal(Unmarshaller unmarshaller, Class<T> clazz, String xmlLocation) throws JAXBException {
StreamSource xml = new StreamSource(xmlLocation);
#SuppressWarnings("unchecked")
Response<T> wrapper = (Response<T>) unmarshaller.unmarshal(xml, Response.class).getValue();
return wrapper;
}
And I get this exception reading the response list:
Exception in thread "main" java.lang.ClassCastException: com.sun.org.apache.xerces.internal.dom.ElementNSImpl cannot be cast to org.test.Project
Note: I can't modify the original XML. There are more types other than Project and User.
Thanks to Blaise Doughan and his article I've found the solution.
First we need the Wrapper class provided in the article:
#XmlRootElement
public class Wrapper<T> {
private List<T> items;
public Wrapper() {
items = new ArrayList<T>();
}
public Wrapper(List<T> items) {
this.items = items;
}
#XmlAnyElement(lax=true)
public List<T> getItems() {
return items;
}
}
Then I've modified the Response class in order to use it:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Response<T> {
#XmlElement
protected String status;
#XmlElement
protected Wrapper<T> result;
...
public Response(String status, List<T> result) {
this.status = status;
this.result = new Wrapper<>(result);
}
...
public List<T> getResult() {
return result.getItems();
}
...
}
Finally the unmarshalling code:
JAXBContext context = JAXBContext.newInstance(Response.class, Project.class, User.class, Wrapper.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
StreamSource source = new StreamSource(new File("responseProject.xml"));
Response<Project> responseProject = (Response<Project>)unmarshaller.unmarshal(source);
System.out.println(responseProject.getStatus());
for (Project project:responseProject.getResult()) System.out.println(project);
source = new StreamSource(new File("responseUser.xml"));
Response<User> responseUser = (Response<User>)unmarshaller.unmarshal(source);
System.out.println(responseUser.getStatus());
for (User user:responseUser.getResult()) System.out.println(user);
I've added the Wrapper class to the context class list.
Alternatively you can add this annotation to the Response class:
#XmlSeeAlso({Project.class, User.class})
Using #XmlSeeAlso({Project.class, User.class}) on Response classes has the drawback of generating some garbage information on each entity in the list: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="userAccount"
<resources>
<links>
<link>
<rel>self</rel>
<uri>http://localhost:8080/salonea-1.0/rest/user-accounts?offset=0&limit=2</uri>
</link>
<link>
<rel>prev</rel>
<uri></uri>
</link>
<link>
<rel>next</rel>
<uri>http://localhost:8080/salonea-1.0/rest/user-accounts?offset=2&limit=2</uri>
</link>
</links>
<collection>
<user-account
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="userAccount">
<accountType>user</accountType>
<activationCode>638f502a0e409348ccc2e36c24907f0</activationCode>
<email>michzio#hotmail.com</email>
<login>michzio</login>
<password>sAmPL3#e</password>
<registrationDate>2015-09-03T17:30:03+02:00</registrationDate>
<userId>1</userId>
</user-account>
<user-account
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="userAccount">
<accountType>user</accountType>
<activationCode>334bc79d142a291894bd71881e38a719</activationCode>
<email>alicja#krainaczarow.com</email>
<login>alicja</login>
<password>zAczka!00</password>
<registrationDate>2015-09-03T17:30:03+02:00</registrationDate>
<userId>2</userId>
</user-account>
</collection>
</resources>
I went through almost all questions related to this topic here. But was not able to get a proper solution.
My issue is as follows:
I created a simple program to unmarshall an xml file for which i had a xsd. I was able to do that successfully. But if i am getting an xml without xsd, how can I get my attributes from that, if the xml looks something like this :
<items>
<item>
<code>12000</code>
<name>Samsung 620</name>
<price>9999</price>
</item>
<item>
<code>15000</code>
<name>NOKIA</name>
<price>19999</price>
</item>
<item>
<code>18000</code>
<name>HTC 620</name>
<price>29999</price>
</item>
</items>
Here I don't have an xsd to generate my classes. How can i proceed? Kindly help me.
Thank You
Below is one way that you could map your use case with a JAXB (JSR-222) implementation:
Items
We will use the following class for the root object and annotate it with #XmlRootElement. The #XmlRootElement annotation tells JAXB that this class should be instantiated if the root element in the document being unmarshalled is items, you can also specify a different name #XmlRootElement(name="foo").
package forum11152046;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Items {
private List<Item> items;
#XmlElement(name="item")
public List<Item> getItems() {
return items;
}
public void setItems(List<Item> items) {
this.items = items;
}
}
Item
In this example I created a class where all the property names correspond directly to the names in the XML document. This means there aren't any annotations that need to be added. If you need to override the default name you can use an annotation such as #XmlElement to do so. I used the #XmlElement annotation to do this in the Items class for the items property.
package forum11152046;
public class Item {
private int code;
private String name;
private int price;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
Demo
package forum11152046;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Items.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum11152046/input.xml");
Items items = (Items) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(items, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<items>
<item>
<code>12000</code>
<name>Samsung 620</name>
<price>9999</price>
</item>
<item>
<code>15000</code>
<name>NOKIA</name>
<price>19999</price>
</item>
<item>
<code>18000</code>
<name>HTC 620</name>
<price>29999</price>
</item>
</items>
If you want to stick with JAXB, you can either write an XML Schema Document on your own to validate such XML (it looks simple but it's just an instance, you need to find out what could change in these documente beforehand) or create a POJO with JAXB annotations matching these nodes. I'm afraid there's no other way. You still have to know well what the format allows.
When I marshall an instance of this class ...
#XmlRootElement
public static class TestSomething<T extends Serializable> {
T id;
public T getId() {
return id;
}
public void setId(T id) {
this.id = id;
}
}
... the following Exception is thrown ...
com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions
java.io.Serializable is an interface, and JAXB can't handle interfaces.
this problem is related to the following location:
at java.io.Serializable
at public java.io.Serializable TestSomething.getId()
at TestSomething
java.io.Serializable does not have a no-arg default constructor.
this problem is related to the following location:
at java.io.Serializable
at public java.io.Serializable TestSomething.getId()
at TestSomething
How can I avoid this (without changing the type parameter to something like <T>)?
You need to use a combination of #XmlElement and #XmlSchemaType:
import java.io.Serializable;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;
#XmlRootElement
public class TestSomething<T extends Serializable> {
T id;
#XmlElement(type=Object.class)
#XmlSchemaType(name="anySimpleType")
public T getId() {
return id;
}
public void setId(T id) {
this.id = id;
}
}
If you run the following:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws JAXBException {
JAXBContext jc = JAXBContext.newInstance(TestSomething.class);
TestSomething<Integer> foo = new TestSomething<Integer>();
foo.setId(4);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(foo, System.out);
}
}
You will get:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<testSomething>
<id xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:int">4</id>
</testSomething>
Here is a guide how to use interfaces with JAXB.
JAXB needs concrete classes, because it has to instantiate them when marshalling from XML. And if T is no concrete class, it can't be instantiated.
Adding #XmlAnyElement to the 'id' field (along with #XmlAccessorType(XmlAccessType.FIELD) annotation at class level) or adding the same for getter will resolve this. (But it makes the xml element's type any.)