Jackson XML - deserializing empty classes and polymorphism - java

I have the following interface:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
#JsonSubTypes({
#JsonSubTypes.Type(value = EmptyProxy.class, name = "empty"),
... other types not included ...
})
public interface Proxy {
}
I have the following implementation:
#JsonTypeName("empty")
public static class EmptyProxy implements Proxy {
}
As you can see, it is just an empty class. I left the other (working) implementations out of this example.
I have the following container data class:
public static class Data {
#JacksonXmlProperty(localName = "name")
private String name;
#JacksonXmlProperty(localName = "proxy")
private Proxy proxy;
}
Deserializing EmptyProxy does not seem to work. For example:
final ObjectMapper mapper = new XmlMapper().registerModule(new JacksonXmlModule());
final Data data = mapper.readValue("<data><name>my-name</name><proxy><empty/></proxy></data>", Data.class);
This gives the following exeption:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of Test$EmptyProxy out of VALUE_NULL token
at [Source: java.io.StringReader#59ec2012; line: 1, column: 42] (through reference chain: Data["proxy"])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:857)
Is this a bug in Jackson? FWIW, when I add a dummy field to EmptyProxy, it works.
update
I tried with JAXB only, and get the same result. Code:
public static class Data {
#XmlElement(name = "name")
private String name;
#XmlElements({
#XmlElement(type = EmptyProxy.class, name = "empty")
})
private Proxy proxy;
}
public interface Proxy {
}
#XmlType(name = "empty")
public static class EmptyProxy implements Proxy {
}
public static void main(String[] a) throws IOException {
final ObjectMapper mapper = new XmlMapper()/*.registerModule(new JacksonXmlModule())*/.registerModule(new JaxbAnnotationModule());
final Data data = mapper.readValue("<data><name>my-name</name><proxy><empty></empty></proxy></data>", Data.class);
}

I have created a bug entry for this. See http://github.com/FasterXML/jackson-dataformat-xml/issues/169.

Related

How to deserialize JSON to interface?

I have trouble with deserialization JSON to some of classes ChildA, ChildB and etc. that implements Basic interface in following example.
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = InstagramUser.class, name = "ChildA")
})
public interface Basic {
getName();
getCount();
}
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonTypeName("ChildA")
public class ChildA implements Basic { ... }
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonTypeName("ChildB")
public class ChildB implements Basic { ... }
...
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Response<E extends Basic> {
#JsonProperty("data")
private List<E> data;
public List<E> getData() {
return data;
}
public void setData(List<E> data) {
this.data = data;
}
}
// deserialization
HTTPClient.objectMapper.readValue(
response,
(Class<Response<ChildA>>)(Class<?>) Response.class
)
Exception is: com.fasterxml.jackson.databind.JsonMappingException: Unexpected token (END_OBJECT), expected FIELD_NAME: missing property 'type' that is to contain type id (for class Basic)
Expected JSON is like this:
{
"data": [{ ... }, ...]
}
There is no property that is presented in all type objects so they are completely different. But as you can see on readValue line I know what is expected type. How to structure JsonTypeInfo and JsonSubTypes annotaions to deserialize JSON as expected class?
I kinda had the same problem as you, based in the reading here: Jackson Deserialize Abstract Classes I created my own solution, it basically consists of creating my own deserializer, the trick is to use/identify a specific property within JSON to know which instance type should be returned from deserialization, example is:
public interface Basic {
}
First Child:
public class ChildA implements Basic {
private String propertyUniqueForThisClass;
//constructor, getters and setters ommited
}
SecondChild:
public class ChildB implements Basic {
private String childBUniqueProperty;
//constructor, getters and setters ommited
}
The deserializer (BasicDeserializer.java) would be like:
public class BasicDeserializer extends StdDeserializer<Basic> {
public BasicDeserializer() {
this(null);
}
public BasicDeserializer(final Class<?> vc) {
super(vc);
}
#Override
public Basic deserialize(final JsonParser jsonParser,
final DeserializationContext deserializationContext)
throws IOException {
final JsonNode node = jsonParser.getCodec().readTree(jsonParser);
final ObjectMapper mapper = (ObjectMapper) jsonParser.getCodec();
// look for propertyUniqueForThisClass property to ensure the message is of type ChildA
if (node.has("propertyUniqueForThisClass")) {
return mapper.treeToValue(node, ChildA.class);
// look for childBUniqueProperty property to ensure the message is of type ChildB
} else if (node.has("childBUniqueProperty")) {
return mapper.treeToValue(node, ChildB.class);
} else {
throw new UnsupportedOperationException(
"Not supported class type for Message implementation");
}
}
}
Finally, you'd have an utility class (BasicUtils.java):
private static final ObjectMapper MAPPER;
// following good software practices, utils can not have constructors
private BasicUtils() {}
static {
final SimpleModule module = new SimpleModule();
MAPPER = new ObjectMapper();
module.addDeserializer(Basic.class, new BasicDeserializer());
MAPPER.registerModule(module);
}
public static String buildJSONFromMessage(final Basic message)
throws JsonProcessingException {
return MAPPER.writeValueAsString(message);
}
public static Basic buildMessageFromJSON(final String jsonMessage)
throws IOException {
return MAPPER.readValue(jsonMessage, Basic.class);
}
For testing:
#Test
public void testJsonToChildA() throws IOException {
String message = "{\"propertyUniqueForThisClass\": \"ChildAValue\"}";
Basic basic = BasicUtils.buildMessageFromJSON(message);
assertNotNull(basic);
assertTrue(basic instanceof ChildA);
System.out.println(basic);
}
#Test
public void testJsonToChildB() throws IOException {
String message = "{\"childBUniqueProperty\": \"ChildBValue\"}";
Basic basic = BasicUtils.buildMessageFromJSON(message);
assertNotNull(basic);
assertTrue(basic instanceof ChildB);
System.out.println(basic);
}
The source code can be found on: https://github.com/darkstar-mx/jsondeserializer
I find not exactly solution but a workaround. I used custom response class ChildAResponse and passed it to ObjectMapper.readValue() method.
class ChildAResponse extends Response<ChildA> {}
// deserialization
HTTPClient.objectMapper.readValue(
response,
ChildAResponse.class
)
So JsonTypeInfo and JsonSubTypes annotations on the interface are no longer needed.

How does Jackson deserialize json into a generic type?

class Person:
#Data
public class Person<T extends Parent> implements Serializable {
private static final long serialVersionUID = 7822965786010192978L;
private static final ObjectMapper objectMapper = new ObjectMapper();
private String id;
private T people; // change (String peopleInfo) to object extends Parent after read data from database
private String peopleInfo; // change (T people) to string and save in the dataBase as an string
#SneakyThrows
public void setPeople(T people) {
this.people = people;
peopleInfo = objectMapper.writeValueAsString(people);
}
#SneakyThrows
public void setPeopleInfo(String peopleInfo) {
this.peopleInfo = peopleInfo;
if (!Strings.isNullOrEmpty(peopleInfo)) {
people = objectMapper.readValue(peopleInfo, new TypeReference<T>() {});
}
}
}
class Parent:
#Data
public class Parent implements Serializable {
private static final long serialVersionUID = 2092353331893381153L;
private String name;
}
class Child:
#Data
public class Child extends Parent {
private static final long serialVersionUID = 3318503314202792570L;
private String pocketMoney;
}
and the test function: I want to save the persnInfo to the database as String and change this string to an object people automatically after reading data from database.
#Test
public void testReadData() throws Exception {
Child child = new Child();
child.setName("_child");
child.setPocketMoney("10$");
Person<Child> person = new Person<>();
person.setId("1");
person.setPeople(child);
// assume this json was read from database
String json = person.getPeopleInfo();
System.out.println(json);
Person<Child> readPerson = new Person<>();
readPerson.setId("1");
readPerson.setPeopleInfo(json);
Child readChild = readPerson.getPeople();
System.out.println(readChild.getPocketMoney());
}
The error happened:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "pocketMoney" (class pers.test.common.objmapper.Parent), not marked as ignorable (one known property: "name"])
at [Source: {"name":"_child","pocketMoney":"10$"}; line: 1, column: 33] (through reference chain: pers.test.common.objmapper.Parent["pocketMoney"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:51)
I have no idea to solve this, anyone can help me make it pass without changing the testReadData()' but change the modelPeople`.
Your problem is here:
new TypeReference<T>()
This doesn't do what you expect it to do. Java generics are erased at runtime; therefore the above statement is basically new TypeReference<Object>.
In other words - the fact that you declared
Person<Child> readPerson = new Person<>();
to expect Child objects is simply not sufficient!
You probably have to pass the specific class Child.class to the code that maps JSON strings back. For further information, look here.

SimpleXML framework: element with element or text

I must to parse XML which can have two forms:
<Command><Variable/></Command>
or:
<Command>some text</Command>
How can I do this? When I try to declare both #Element and #Text in class responsible for Command parsing then exception is thrown when I try to parse XML to instance of this class.
My current version of code:
#Root(name = "Command", strict = false)
public class AppCommand {
#Element(name = "Variable", required = false)
#Getter
private Variable variable;
#Text(required = false)
#Getter
private String content;
}
And exception is: Text annotation #org.simpleframework.xml.Text(required=false, empty=, data=false) on field 'content' private java.lang.String com.example.AppCommand.content used with elements in class com.example.AppCommand
My solution (not beautiful, but works and doesn't require much work to implement):
private static class SerializerWithPreprocessor extends Persister {
public SerializerWithPreprocessor(RegistryMatcher matcher, Format format) {
super(matcher, format);
}
#Override
public <T> T read(Class<? extends T> type, String source) throws Exception {
source = source.replaceFirst("<Command (.*)>([[\\w||[+=]]&&[^<>]]+)</Command>", "<Command $1><Content>$2</Content></Command>");
return super.read(type, source);
}
}
So I just created new Serializer class. This class use regular expressions to change Text element inside Command into normal Element. Then I can use:
#Root(name = "Command", strict = false)
public class AppCommand {
#Element(name = "Variable", required = false)
#Getter
private Variable variable;
#Element(name = "Content", required = false)
#Getter
private String content;
}
and during deserialization everything works like I wanted to.
Yes, Simple can't deal with this.
Command.java:
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;
import org.simpleframework.xml.Text;
#Root
public class Command {
#Element(required = false, name = "Variable")
private Variable variable;
#Text(required = false)
private String text;
}
Variable.java:
class Variable {
}
SOPlayground.java:
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
public class SOPlayground {
public static void main(String[] args) throws Exception {
Serializer serializer = new Persister();
String xml1 = "<Command><Variable/></Command>";
String xml2 = "<Command>some text</Command>";
serializer.validate(Command.class, xml1);
serializer.validate(Command.class, xml2);
}
}
This does compile but it does not run:
Exception in thread "main" org.simpleframework.xml.core.TextException: Text annotation #org.simpleframework.xml.Text(data=false, required=false, empty=) on field 'text' private java.lang.String de.lhorn.so.Command.text used with elements in class de.lhorn.so.Command
It looks like can not have both #Element and #Text members.

Using #JsonIdentityInfo without annotations

I use Jackson 2.2.3 to serialize POJOs to JSON. Then I had the problem, that I couldn't serialize recursive structures...I solved this problem by using #JsonIdentityInfo => works great.
But, I don't want this annotation on the top of my POJO.
So my question is: Is there any other possibility to set the default behavior of my ObjectMapper to use the feature for every POJO?
So I want to transform this annotation code
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#id")
to something like
ObjectMapper om = new ObjectMapper();
om.setDefaultIdentityInfo(ObjectIdGenerators.IntSequenceGenerator.class, "#id");
Any ideas?
You can achieve that using the Jackson mix-in annotations or the Jackson annotation introspector.
Here is an example showing both methods:
public class JacksonJsonIdentityInfo {
#JsonIdentityInfo(
generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "#id")
static class Bean {
public final String field;
public Bean(final String field) {this.field = field;}
}
static class Bean2 {
public final String field2;
public Bean2(final String field2) {this.field2 = field2;}
}
#JsonIdentityInfo(
generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "#id2")
static interface Bean2MixIn {
}
static class Bean3 {
public final String field3;
public Bean3(final String field3) {this.field3 = field3;}
}
static class MyJacksonAnnotationIntrospector extends JacksonAnnotationIntrospector {
#Override
public ObjectIdInfo findObjectIdInfo(final Annotated ann) {
if (ann.getRawType() == Bean3.class) {
return new ObjectIdInfo(
PropertyName.construct("#id3", null),
null,
ObjectIdGenerators.IntSequenceGenerator.class,
null);
}
return super.findObjectIdInfo(ann);
}
}
public static void main(String[] args) throws JsonProcessingException {
final Bean bean = new Bean("value");
final Bean2 bean2 = new Bean2("value2");
final Bean3 bean3 = new Bean3("value3");
final ObjectMapper mapper = new ObjectMapper();
mapper.addMixInAnnotations(Bean2.class, Bean2MixIn.class);
mapper.setAnnotationIntrospector(new MyJacksonAnnotationIntrospector());
System.out.println(mapper.writeValueAsString(bean));
System.out.println(mapper.writeValueAsString(bean2));
System.out.println(mapper.writeValueAsString(bean3));
}
}
Output:
{"#id":1,"field":"value"}
{"#id2":1,"field2":"value2"}
{"#id3":1,"field3":"value3"}
After several months and a lot of research, I've implemented my own solution to keep my domain clear of jackson dependencies.
public class Parent {
private Child child;
public Child getChild(){return child;}
public void setChild(Child child){this.child=child;}
}
public class Child {
private Parent parent;
public Child getParent(){return parent;}
public void setParent(Parent parent){this.parent=parent;}
}
First, you have to declare each of your entities of the bidirectional relationship:
public interface BidirectionalDefinition {
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id", scope=Parent.class)
public interface ParentDef{};
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id", scope=Child.class)
public interface ChildDef{};
}
After that, the object mapper can be automatically configured:
ObjectMapper om = new ObjectMapper();
Class<?>[] definitions = BidirectionalDefinition.class.getDeclaredClasses();
for (Class<?> definition : definitions) {
om.addMixInAnnotations(definition.getAnnotation(JsonIdentityInfo.class).scope(), definition);
}

Serializing and deserializing Java generics using Jackson: StackOverflowError

I'm currently working on a model which uses generics and is little complicated. I understand that similar questions have been answered but none of them clearly answers mine.
Here is my model:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.WRAPPER_OBJECT, property = "type")
#JsonSubTypes(
{
#Type(value = Cls2.class, name = "Cls2")
})
abstract class Cls1<T> implements Serializable
{
private T myObj;
public T getMyObj()
{
return myObj;
}
public Cls1(T obj)
{
myObj = obj;
}
#JsonTypeName("Cls2")
public static class Cls2<E extends Int1> extends Cls1<E> implements Serializable
{
public Cls2()
{
super(null);
}
}
}
#JsonTypeName("ChildContainer")
class ChildContainer extends ParentContainer<OtherBean>
{
}
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.WRAPPER_OBJECT, property = "type")
#JsonSubTypes(
{
#Type(value = ChildContainer.class, name = "ChildContainer")
})
class ParentContainer<T extends RootBean> implements Int1
{
}
#JsonTypeName("OtherBean")
class OtherBean extends RootBean
{
}
#JsonTypeName("RootBean")
class RootBean implements Int1
{
}
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.WRAPPER_OBJECT, property = "type")
#JsonSubTypes(
{
#Type(value = RootBean.class, name = "RootBean"),
#Type(value = OtherBean.class, name = "OtherBean")
})
interface Int1 extends Serializable
{
}
My goal is to serialize and deserialze using jackson as follows:
public static void main(String[] args) throws Exception
{
Cls2<ChildContainer> req = new Cls2<ChildContainer>();
File file = new File("==some-file-path==");
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(file, req);
//read it back using mapper.readValue(file, clazz) --Not sure about this
}
I get the following java.lang.StackOverflowError during the serialization:
Exception in thread "main" java.lang.StackOverflowError
at java.lang.Class.getDeclaringClass(Native Method)
at org.codehaus.jackson.map.type.TypeBindings._resolveBindings(TypeBindings.java:290)
at org.codehaus.jackson.map.type.TypeBindings._resolve(TypeBindings.java:221)
at org.codehaus.jackson.map.type.TypeBindings.findType(TypeBindings.java:138)
at org.codehaus.jackson.map.type.TypeFactory._fromVariable(TypeFactory.java:951)
at org.codehaus.jackson.map.type.TypeFactory._constructType(TypeFactory.java:493)
at org.codehaus.jackson.map.type.TypeFactory.findTypeParameters(TypeFactory.java:423)
at org.codehaus.jackson.map.type.TypeFactory.findTypeParameters(TypeFactory.java:395)
at org.codehaus.jackson.map.type.TypeBindings._resolveBindings(TypeBindings.java:299)
at org.codehaus.jackson.map.type.TypeBindings._resolveBindings(TypeBindings.java:290)
at org.codehaus.jackson.map.type.TypeBindings._resolve(TypeBindings.java:221)
at org.codehaus.jackson.map.type.TypeBindings.findType(TypeBindings.java:138)
at org.codehaus.jackson.map.type.TypeFactory._fromVariable(TypeFactory.java:951)
at org.codehaus.jackson.map.type.TypeFactory._constructType(TypeFactory.java:493)
at org.codehaus.jackson.map.type.TypeFactory.findTypeParameters(TypeFactory.java:423)
at org.codehaus.jackson.map.type.TypeFactory.findTypeParameters(TypeFactory.java:395)
at org.codehaus.jackson.map.type.TypeBindings._resolveBindings(TypeBindings.java:299)
at org.codehaus.jackson.map.type.TypeBindings._resolveBindings(TypeBindings.java:290)
Any help is deeply appreciated.
The stacktrace suggests an infinite recursion in type resolving of Cls2 which extends the class it is by itself nested in. This seems to be a corner case bug in Jackson (report it!). In the meanwhile, extracting Cls2 into a standalone class instead of nesting it in its superclass should solve this problem.

Categories