JAXB Unmarshal List of Interfaces - java

I am trying to wrap 2 Classes, that are built following the Composite pattern, with JAXB.
The whole thing i built like this:
public interface ICriterion {String somemethod(String arg);}
#XmlRootElement(name = "criterion")
#XmlAccessorType(XmlAccessType.FIELD)
public class Criterion implements ICriterion {
#XmlElement String name;
#XmlElement String data;
//getter, setter, somemethod(), Criterion(), Criterion(String, String)
}
#XmlRootElement(name = "criteria")
#XmlAccessorType(XmlAccessType.FIELD)
public class Criteria implements ICriterion {
#XmlAnyElement(lax=true)
private final List<ICriterion> criteria;
//getter, setter, somemethod(),
Criteria(){criteria = new ArrayList<>();}
Criteria(List<ICriterion>)...
}
i took this solution from this answer
It works fine for marshalling - i can provide a Criteria, which contain a list of Criteria and Criterions, it gets marshaled to xml correctly.
What does not work however is unmarshaling a string to an Object
String request =
<criteria>
<criteria>
<criterion>
<name>number1</value>
<data>123123</token>
</criterion>
<criterion>
<name>number2</value>
<data>1223323</token>
</criterion>
</criteria>
<criterion>
<name>number3</value>
<data>1212</token>
</criterion>
</criteria>
JAXBContext ctx = JAXBContext.newInstance(Criteria.class, Criterion.class);
StringReader reader = new StringReader(request);
Criteria wrapped = (Criteria) ctx.createUnmarshaller().unmarshal(reader);
gives:
java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at java.util.AbstractList.add(AbstractList.java:108)
I've tried other ways that are avaiable through online sources like:
#XmlElementRefs({ #XmlElementRef(type=Criterion.class), #XmlElementRef(type=Criteria.class)})
#XmlElements({#XmlElement(type=Criteria.class),#XmlElement(type=Criterion.class)})
which do not give an error, but result in the List being empty instead. The solution to this problem, is using #XmlAnyElement(lax=true), which does not work for me.
My first guess was, that somehow the List is not initialized correctly in my Constructor and JAXB uses some kind of fixed size list.
My countermeasure was to explicitly initialize the list in the no-Args constructor
Criteria(){criteria = new ArrayList<>();}
The problem also is not solved by explicitly making "critera" an ArrayList<> instead of List<>.
What am I missing?

Related

How to add wrapper element to class during the JAXB marshalling

I am trying to create XML using the Moxy Marshalling approach. Everything seems to be working fine except for one small thing. Basically, I would like to know how to add wrapper element to the class itself during the marshalling.
As we are aware we can add #XmlPath("test/name/text()") to add a wrapper to any of the String elements. Also for collection we can make use of #XmlElementWrapper(name="languages"), #XmlElement(name="language"). But these are for the fields within the class. How can I add the wrapper element to the class itself which is being marshalled?
I have the following class:
#XmlRootElement(name = "customer")
#XmlType(name="customer",propOrder={"name","languages"})
#XmlAccessorType(XmlAccessType.FIELD)
public class Customer{
#XmlPath("extension/name/text()")
private String name;
#XmlElementWrapper(name = "languages")
#XmlElement(name = "language")
private List<String> languages;
//Getter, Setter and other constuctors
}
This will produce the output XML something like this:
<customer>
<extension>
<name>Hello</name>
</extension>
<languages>
<language>English</language>
<language>UK English</language>
</languages>
</customer>
However, I would like to add the wrapper element to event the whole class so that output would look something like this: (Notice the whole customer is wrapper within classWrapper)
<classWrapper>
<customer>
<extension>
<name>Hello</name>
</extension>
<languages>
<language>English</language>
<language>UK English</language>
</languages>
</customer>
</classWrapper>
I tried adding the #XmlPath and #XmlElementWrapper to the Customer class but it throws an error as it can be added only to the fields within the class but not to the whole class.
Following is my Main class which would be used for marshalling:
public class HelloWorld{
public static void main(String []args){
Customer customer = new Customer();
List<String> languages = new ArrayList<String>();
languages.add("English");
languages.add("UK English");
customer.setName("Hello");
customer.setLanguages(languages);
JAXBContext context = JAXBContext.newInstance(Customer.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(customer,System.out);
}
}
I am aware that I can write another wrapper class add the custom field to it then use that wrapper class for the marshalling. Actually, I am doing the same as of now. However, trying to find if there is some Moxy approach of doing this similar to how we have #XmlPath and #XmlElementWrapper
Can someone please suggest some form of Moxy Jaxb related approach for achieving the wrapper element to the whole class?
This is a temporary workaround that works as expected for me. Posting the answer here so it can be helpful to someone in the future.
Change the #XmlRootElement("customer") to #XmlRootElement("classWrapper"). So you will get the classWrapper as the outer element.
Then change all the element within the Customer class using the #XmlPath so that all element go under the Customer tag. So overall customer.class would look something like this:
#XmlRootElement(name = "classWrapper")
#XmlType(name="customer",propOrder={"name","languages"})
#XmlAccessorType(XmlAccessType.FIELD)
#Getter
#Setter
public class Customer{
#XmlPath("customer/extension/name/text()")
private String name;
#XmlPath("customer/languages/language/text()")
private List<String> languages;
//Getter, Setter and other constuctors
}
Just a tip while using the #XmlPath:
do not use text() if its not simple type such as String,Date, etc type.
For example if the element is of Custom type with List then do not use /text()
#XmlPath("extension/elements/element")
List<myType> elements;
This will add extension -> elements -> element then content.
<extension>
<elements>
<element></element>
<element></element>
......
</elements>
</extension>
If the elements are of String type then you have to use text()
#XmlPath("extension/elements/text()")
List<String> elements;

Jackson XML unmarshalling and abstract classes

I'm trying to unmarshal the following XML using Jackson 2.4.0 XmlMapper:
<root>
<a/>
<b/>
</root>
...and the following POJO
class Root {
#JacksonXmlElementWrapper(useWrapping = false)
#JsonSubTypes({
#JsonSubTypes.Type(name = "a", value = POJO_A.class),
#JsonSubTypes.Type(name = "b", value = POJO_B.class)
})
public final List<AbstractPOJO> objects = new ArrayList<>();
}
I've also tried with JAXB #XmlElements annotations with the same result, which is:
Unrecognized field "a" (class Root), not marked as ignorable (1 known properties: "objects"])
So it seems that Jackson thinks my list is called "objects" instead of "a" and "b". Normally I fix that by using #JsonProperty("newName") but in this case I expected that to be handled by the #JsonSubtypes or #XmlElements annotations.
As I cannot modify the input XML, is there anything else I can do, either with Jackson, Jackson XML or JAXB annotations?
Update: Forgot to say, the problem is deserializing to the same collection (because I need to keep the order and they can be mixed). Doing it in separate fields works just fine.
When I was trying peeskillet answer and changed Jackson for JAXB unmarshaller, using #XmlElements/#XmlElementRefs worked (#XmlAnyElement did not for some reason, I was getting a list of ElementNSImpl instead of my own classes).
It will be nice to have this working with Jackson, but in the meantime, this is the way to make this work.
UPDATE:
What I did was:
class Root {
#XmlElements({
#XmlElement(name = "a", type = PojoA.class),
#XmlElement(name = "b", type = PojoB.class)
})
public final List<AbstractPOJO> objects = new ArrayList<>();
}
I also added a #XmlRootElement(name = "a") to PojoA, but I think it's not used when you have #XmlElements
"As I cannot modify the input XML, is there anything else I can do, either with Jackson, Jackson XML or JAXB annotations?"
I'm not too familiar with Jackson's Xml features. But with the JAXB Unmarshaller and a little change to your annotations, this can be achieved.
For your List<AbstractPojo> you can use the #XmlAnyElement(lax = true).
With lax = true:
If true, when an element matches a property marked with XmlAnyElement is known to JAXBContext (for example, there's a class with XmlRootElement that has the same tag name, or there's XmlElementDecl that has the same tag name), the unmarshaller will eagerly unmarshal this element to the JAXB object, instead of unmarshalling it to DOM
Which basically means that if we annotate PojoA and PojoB with #XmlRootElement and pass the element name as the name attribute (#XmlRootElement(name = "a")), by definition, this should work.
Let's give it a shot:
public abstract class AbstractPojo {
// note this class is not annotated. It will be known in the context
// as we're explicitly using the type int the Root class
}
#XmlRootElement(name = "a")
public class PojoA extends AbstractPojo {
}
#XmlRootElement(name = "b")
public class PojoB extends AbstractPojo {
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "root")
public class Root {
#XmlAnyElement(lax = true)
protected List<AbstractPojo> objects;
public List<AbstractPojo> getObjects() {
if (objects == null) {
objects = new ArrayList<>();
}
return this.objects;
}
}
Using the following xml file to test
<?xml version="1.0" encoding="UTF-8"?>
<root>
<a/> <b/> <b/> <a/> <a/> <b/> <b/>
</root>
And the following test program
public class JaxbTest {
private static final String FILE_NAME = "test.xml";
public static void main(String[] args) throws Exception {
JAXBContext context = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
File f = new File(FILE_NAME);
Root root = (Root)unmarshaller.unmarshal(f);
List<AbstractPojo> list = root.getObjects();
for (AbstractPojo p : list) {
System.out.print( p instanceof PojoA ? "a " : "b ");
}
System.out.println();
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
We get the result we're looking for.
a b b a a b b // along with the xml file content we marshal for testing
Here are some good resources:
JAXB tutorial
Oracle JAXB tutorial
#XmlAnyElement
Blaise Doughan's blog is always a good resource
UPDATE
Ok I see why you were getting ElementNSImpl. This doesn't work for me, I'm getting ElementNSImpl objects. Do you know what's happening?
Yea I see what's happening. Firstly, I compiled an xsd with xjc, and it created an ObjectFactory for me, which declared the elements. That's why it way working for me.
If you don't do this, then you should explicitly put the PojoA and PojoB into the context.
JAXBContext.newInstance(Root.class, PojoA.class, PojoB.class);

Jackson JSON XML - different names when serializing to XML

I'd like to have different name for my element when it's serialized to XML (for example "fooXml") and different for JSON (for example "fooJson"). Is it possible?
I'm using XML annotations like:
#XmlElements({
#XmlElement(type = Foo.class, name = "fooXml"),
})
private SortedSet<Foo> fooSet;
I've tried already #JsonProperty, with without any luck.
I've also tried exporting it to getter method, like:
#XmlElement(type = Foo.class, name = "fooXml")
#JsonProperty(value = "fooJson")
public List<Foo> getFooList() {
return new ArrayList<>(fooSet);
}
But it's always ignoring JSON annotations and serializing to XML form (fooXml name).
How shall I do it?
edit: I'm using Jersey-json.
Alright, I had a need for this same functionality and found a solution that works for this:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
#JsonProperty("MyJsonName")
#JacksonXmlProperty(localName = "MyXmlName")
private MyProperty myProperty;
Works for me, and myProperty will be in the 'MyJsonName' field in Json and the 'MyXmlName' in XML.

Fetching data from jdo datastore

I have created two classes on client-side and they are stored using GWT JDO.
The parent class looks like:
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Park implements Serializable{
#PrimaryKey
#Persistent
private String parkId;
//...
#Persistent(mappedBy = "park", defaultFetchGroup = "true")
private List<Facility> facilityList;
// other stuff
and the child looks like:
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Facility implements Serializable{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
#Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
private String encodedKey;
#Persistent
private Park park;
// other stuff
And on server side, I have a method to fetch everything:
public Park[] getParks(){
PersistentManager pm = getPersistentManager();
ArrayList<Park> parkList = new ArrayList<Park>();
try {
Query q = pm.newQuery(Park.class);
List<Park> parks = (List<Park>) q.execute();
for(Park p:parks)
parkList.add(p);
} finally {
pm.close();
}
return parkList.toArray(new Park[parkList.size()]);
}
When I call this method, it throws an exception:
com.google.gwt.user.client.rpc.SerializationException: Type 'org.datanucleus.store.types.sco.backed.ArrayList' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.
I can't figure out whats wrong. Any suggestion is welcome.
I had this problem a while back and it was quite frustrating. And I'm also quite sure your problem isn't because you imported the wrong package. You probably already solved this, but if anyone else need to solve the problem.
Try the below example code:
public Park[] getParks(){
PersistentManager pm = getPersistentManager();
List<Park> parkList = null;
try {
Query q = pm.newQuery(Park.class);
parkList = (List<Park>) q.execute();
parkList = pm.detachCopyAll(parkList);
} finally {
pm.close();
}
return parkList.toArray(new Park[parkList.size()]);
}
In the example above I've changed ArrayList to just List (not sure if necessary) and use detachCopyAll to copy the list so it is not connected to the datastore any more, this is the magic that removed the error for me.
There's an annotation that can be used, i quote from the documentation:
You can modify an object after the PersistenceManager has been closed
by declaring the class as "detachable." To do this, add the detachable
attribute to the #PersistenceCapable annotation:
#PersistenceCapable(detachable="true")
Not sure though if this means that you can do, what you originally did, but it's worth a try because I don't think my solution is "pretty".
Change the ArrayList declaration to use Java's ArrayList:
java.util.ArrayList<Park> parkList = new java.util.ArrayList<Park>();
GWT cannot serialize objects that do not have a default constructor and the ArrayList you have imported (org.datanucleus.store.types.sco.backed.ArrayList) doesn't provide a default constructor, making serialization fail.
It is possible that you imported this class by mistake in which case you can just change the import declaration. But if you are using it somewhere else, then you will have to use the full qualifier as shown.

java.util.List is an interface, and JAXB can't handle interfaces

I seemed to get the following exception when trying to deploy my application:
Caused by: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions
java.util.List is an interface, and JAXB can't handle interfaces.
this problem is related to the following location:
at java.util.List
at private java.util.List foobar.alkohol.register.webservice.jaxws.GetRelationsFromPersonResponse._return
at foobar.alkohol.register.webservice.jaxws.GetRelationsFromPersonResponse
java.util.List does not have a no-arg default constructor.
this problem is related to the following location:
at java.util.List
at private java.util.List foobar.alkohol.register.webservice.jaxws.GetRelationsFromPersonResponse._return
at foobar.alkohol.register.webservice.jaxws.GetRelationsFromPersonResponse
My code worked just well until I changed the return type from List to List<List<RelationCanonical>>
Here is the partial webservice:
#Name("relationService")
#Stateless
#WebService(name = "RelationService", serviceName = "RelationService")
#SOAPBinding(style = SOAPBinding.Style.DOCUMENT, use = SOAPBinding.Use.LITERAL, parameterStyle = SOAPBinding.ParameterStyle.WRAPPED)
public class RelationService implements RelationServiceLocal {
private boolean login(String username, String password) {
Identity.instance().setUsername(username);
Identity.instance().setPassword(password);
Identity.instance().login();
return Identity.instance().isLoggedIn();
}
private boolean logout() {
Identity.instance().logout();
return !Identity.instance().isLoggedIn();
}
#WebMethod
public List<List<RelationCanonical>> getRelationsFromPerson(#WebParam(name = "username")
String username, #WebParam(name = "password")
String password, #WebParam(name = "foedselsnummer")
String... foedselsnummer) {
......
......
......
}
I have also tried by removing the #SOAPBinding and trying default, but the same result occurs.
Appreciate any help
UPDATE
I want to note out something. I changed all List to ArrayList, and then it compiled. The reason why I say compiled and not worked is because it behaves strange. I get an Object of type:
RelationServiceStub.ArrayList
but the object has no get methods or does not behave as a List either. I also tried to cast it to a List but that didnt work.
Note that this is after I have used Axis 2 and wsdl2java So yes, now it compiles, but I dont know how to get the data out.
In my understanding, you will not be able to process a plain List via JAXB, as JAXB has no idea how to transform that into XML.
Instead, you will need to define a JAXB type which holds a List<RelationCanonical> (I'll call it Type1), and another one to hold a list of those types, in turn (as you're dealing with a List<List<...>>; I'll call this type Type2).
The result could then be an XML ouput like this:
<Type2 ...>
<Type1 ...>
<RelationCanonical ...> ... </RelationCanonical>
<RelationCanonical ...> ... </RelationCanonical>
...
</Type1>
<Type1>
<RelationCanonical ...> ... </RelationCanonical>
<RelationCanonical ...> ... </RelationCanonical>
...
</Type1>
...
</Type2>
Without the two enclosing JAXB-annotated types, the JAXB processor has no idea what markup to generate, and thus fails.
--Edit:
What I mean should look somewhat like this:
#XmlType
public class Type1{
private List<RelationCanonical> relations;
#XmlElement
public List<RelationCanonical> getRelations(){
return this.relations;
}
public void setRelations(List<RelationCanonical> relations){
this.relations = relations;
}
}
and
#XmlRootElement
public class Type2{
private List<Type1> type1s;
#XmlElement
public List<Type1> getType1s(){
return this.type1s;
}
public void setType1s(List<Type1> type1s){
this.type1s= type1s;
}
}
You should also check out the JAXB section in the J5EE tutorial and the Unofficial JAXB Guide.
If that suits your purpose you can always define an array like this:
YourType[]
JAXB can certainly figure out what that is and you should be able to immediatly use it client side. I would also recommend you to do it that way, since you should not be able to modify the array retrieved from a server via a List but via methods provided by the web service
If you want to do this for any class.
return items.size() > 0 ? items.toArray((Object[]) Array.newInstance(
items.get(0).getClass(), 0)) : new Object[0];
You can use "ArrayList" instead of inner "List"

Categories

Resources