I am facing issues marshalling and unmarshalling collections using MOXy. Collection may hold elements of any type. In the test case below I am trying to marshal and then unmarshall colection of Strings.
Test generates following output:
Going with type: APPLICATION_XML
Original XmlCollection: [one, two, three]
Marshaled as application/xml: <?xml version="1.0" encoding="UTF-8"?><collection>onetwothree</collection>
Unmarshaled XmlCollection: []
Going with type: APPLICATION_JSON
Original XmlCollection: [one, two, three]
Marshaled as application/json: {"value":["one","two","three"]}
Unmarshaled XmlCollection: []
In the XML case I would expect collection elements to be serialized as nested list xml elements holding values, but it is not so.
In the case of JSON genereated code looks good but I would expect object to get unmarshalled properly, which didn't happen.
When I run test with Collection of SimpleObject (uncomment lines marked as Simple object) everything works like a sharm and output produced is following:
Going with type: APPLICATION_XML
Original XmlCollection: [stringField='one', stringField='two', stringField='three']
Marshaled as application/xml: <?xml version="1.0" encoding="UTF-8"?><collection><simpleObject><stringField>one</stringField></simpleObject><simpleObject><stringField>two</stringField></simpleObject><simpleObject><stringField>three</stringField></simpleObject></collection>
Unmarshaled XmlCollection: [stringField='one', stringField='two', stringField='three']
Going with type: APPLICATION_JSON
Original XmlCollection: [stringField='one', stringField='two', stringField='three']
Marshaled as application/json: {"simpleObject":[{"stringField":"one"},{"stringField":"two"},{"stringField":"three"}]}
Unmarshaled XmlCollection: [stringField='one', stringField='two', stringField='three']
Appreciate any clue why it happens and how this mapping must be done properly.
Test case:
public class TestCase {
// Element name holding value of primitive types
public static final String VALUE_ELEMENT = "value";
// Attribute prefix in JSON
public static final String ATTRIBUTE_PREFIX = "@";
public static void main(String... args) {
try {
for (MediaType type : new MediaType[]{MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) {
System.out.println("Going with type: " + type);
JAXBContext context = (JAXBContext) JAXBContextFactory.createContext(
new Class[]{
XmlCollection.class,
// Simple Object SimpleObject.class,
},
Collections.emptyMap());
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, type);
marshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false);
marshaller.setProperty(MarshallerProperties.JSON_ATTRIBUTE_PREFIX, ATTRIBUTE_PREFIX);
marshaller.setProperty(MarshallerProperties.JSON_VALUE_WRAPPER, VALUE_ELEMENT);
marshaller.setProperty(UnmarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME, true);
XmlCollection original = new XmlCollection(Arrays.asList("one", "two", "three"));
// Simple object XmlCollection original = new XmlCollection(Arrays.asList(new SimpleObject("one"), new SimpleObject("two"), new SimpleObject("three")));
System.out.println("Original " + original.toString());
StreamResult result = new StreamResult(new StringWriter());
marshaller.marshal(original, result);
String generated = result.getWriter().toString();
System.out.println("Marshaled as " + type.getMediaType() + ": " + generated);
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, type);
unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false);
unmarshaller.setProperty(UnmarshallerProperties.JSON_ATTRIBUTE_PREFIX, ATTRIBUTE_PREFIX);
unmarshaller.setProperty(UnmarshallerProperties.JSON_VALUE_WRAPPER, VALUE_ELEMENT);
unmarshaller.setProperty(UnmarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME, true);
XmlCollection unmarshaled = unmarshaller.unmarshal(new StreamSource(new StringReader(generated)), XmlCollection.class).getValue();
System.out.println("Unmarshaled " + unmarshaled.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
@XmlRootElement(name = "collection")
@XmlAccessorType(XmlAccessType.FIELD)
public static class XmlCollection<T> {
@XmlAnyElement(lax = true)
private Collection collection = new ArrayList();
public XmlCollection() {
}
public XmlCollection(Collection<T> collection) {
this.collection.addAll(collection);
}
public Collection<T> getCollection() {
return collection;
}
@Override
public String toString() {
return "XmlCollection: " + collection;
}
}
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public static final class SimpleObject{
@XmlElement
public String stringField;
public SimpleObject(){}
public SimpleObject(String stringField){
this.stringField = stringField;
}
public String getStringField() {
return stringField;
}
public void setStringField(String stringField) {
this.stringField = stringField;
}
@Override
public String toString() {
return "stringField='" + stringField + '\'';
}
}
}