DatBear DatBear - 16 days ago 5
JSON Question

Jackson Json with nested parametric classes

Listing:

import java.util.List;

public class Listing<T> {
List<Thing<T>> children;

public List<Thing<T>> getChildren() {
return children;
}

public void setChildren(List<Thing<T>> children) {
this.children = children;
}
}


Thing:

public class Thing<T> {

private String type;
private T data;

public T getData() {
return data;
}

public void setData(T data) {
this.data = data;
}

public String getType() {
return type;
}

public void setType(String type) {
this.type = type;
}
}


Link:

public class Link {
private String author;

public String getAuthor() {
return author;
}

public void setAuthor(String author) {
this.author = author;
}
}


and here's an example of serialization and deserialization...

public static void main(String[] args) throws IOException {
Link link1 = new Link();
link1.setAuthor("JohnDoe");

Link link2 = new Link();
link2.setAuthor("MaryJane");

List<Thing<Link>> things = new ArrayList<Thing<Link>>();

Thing<Link> thing1 = new Thing();
thing1.setData(link1);
thing1.setType("t3");

Thing<Link> thing2 = new Thing();
thing2.setData(link2);
thing2.setType("t3");

things.add(thing1);
things.add(thing2);

Listing<Link> listing = new Listing<Link>();
listing.setChildren(things);


Thing<Listing> thing = new Thing<Listing>();
thing.setType("listing");
thing.setData(listing);

File jsonFile = new File("src/testMap.txt");
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(jsonFile, thing);

//String jsonString = "{\"type\":\"listing\",\"data\":{\"children\":[{\"type\":\"t3\",\"data\":{\"author\":\"JohnDoe\"}},{\"type\":\"t3\",\"data\":{\"author\":\"MaryJane\"}}]}}";
JavaType jsonType = mapper.getTypeFactory().constructParametricType(Thing.class, Listing.class);
Thing<Listing> readThing = mapper.readValue(jsonFile, jsonType);


}


The problem that I'm having is that the Things contained in the Listing in the sample code above are not parametrized with Link, so their data field is returned as an Object (which is actually LinkedHashMap).

I want to be able to do something like this:

List<Thing<Link>> readListingChildren = readThing.getData().getChildren();
String author = readListingChildren.get(0).getData().getAuthor();


My question is, how would I get this to work using Jackson json?

Note: there will be multiple different types of objects contained by Things, and a Thing's data member's type is defined (or should be defined) by the data object's "type" field, using strings such as t1, t2, t3, etc. which map to different classes.

Answer

To achieve a serialized String like

{
    "data":{
        "type":"listing",
        "children":[
            {
                    "data":{
                    "type":"t3",
                    "author":"JohnDoe"
                }
            },
            {
                "data":{
                    "type":"t3",
                    "author":"MaryJane"
                }
            }
        ]
    }
}

and to use the type information to correctly deserialize the concrete class you may use

@JsonTypeName("listing")
public class Listing<T> {
    List<Thing<T>> children;

    public List<Thing<T>> getChildren() {
        return children;
    }

    public void setChildren(final List<Thing<T>> children) {
        this.children = children;
    }
}

public class Thing<T> {
    private T data;

    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
    @JsonSubTypes({
      @JsonSubTypes.Type(Link.class),
      @JsonSubTypes.Type(Listing.class)
    })
    public T getData() {
        return data;
    }

    public void setData(final T data) {
        this.data = data;
    }
}

@JsonTypeName("t3")
public class Link {
    private String author;

    public String getAuthor() {
        return author;
    }

    public void setAuthor(final String author) {
        this.author = author;
    }
}