phil_20686
phil_20686

Reputation: 4080

(de)serialising Nested Generics in Jackson

So I am seeking to deserialise and serialise an object using Jackson. The object has a hierarchy, and deep down in the hierachy there is a

List<T>

where T is either a String, or one of the Java number classes.

Deserialising generic lists has been a common topic on these forums, and I know how to build a mapper that will deserialise or serialise a list using mapper.readValue.

What I don't know how to do is make is so that when I call the mapper on the top level, which doesn't know explicitly about the parametrisation of the classes that it contains, such that it will know how to call the custom deserialiser when it gets to the bottom level class that contains the parameterised list.

A simple example with getters/setters/constructors ommitted:

class A {
    String name;
    B thing;
}

class B {
    String command;
    List<C<?>> conditions;
}

class C<T> {
    String type;
    List<T> parameters
}

And I want to write a Jackson command that serialises A in one go. I have been using a method attached to A:

public String toJSON() throws JsonProcessingException{
    ObjectMapper mapper = new ObjectMapper();
    mapper.enableDefaultTyping();
    return mapper.writeValueAsString(this);
}

but this has the known issue of generic collections losing their type information and not being deserialisable.

I can use the advice here to deserialise a given class with a generic parameter, but I don't see how to combine these solutions. I was hoping that Jackson has some what that I an write a custom deserialiser for C, and it can use that when it reaches that class type, and otherwise it can use the normal serialiser, which works fine for the other classes.

Upvotes: 1

Views: 3158

Answers (1)

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 280122

When Jackson is deserializing your JSON into an object of type A, it sees the following when deserializing for its B field

List<C<?>> conditions;

Neither you (the programmer), nor Jackson know what type C's parameters field should have. As such, it doesn't have enough information to deserialize like you would want it. Instead, it will use its default type which I believe is a LinkedHashMap (or something similar).

Basically, your only option is to give it hints. (Custom) Serialize the object hierarchy with a String value of the Class to deserialize to. You can then have a different strategy for each of those types in your custom deserializer.

Since you only have a few types, String or one of the Number classes, you might be better served writing a generic class C and X non generic classes for each of the types

class C<T> {}
class StringC extends C<String> {}
class IntegerC extends C<Integer> {}

and use some polymorphic type handling to serialize/deserialize.

Upvotes: 4

Related Questions