stefs
stefs

Reputation: 18549

Jackson Polymorphic Deserialisation (where the class depends on the JSON key)

TL;DR

basically, my problem is that i have a list of wrapper objects

{"stuff": [
  {"foobar" : {someObjectOfTypeA}},
  {"barfoo" : {someObjectOfTypeB}},
  {"foobar" : {someObjectOfTypeA}}
]}

and the type of someObjectOfTypeX depends on the value of the key "foobar" or "barfoo". how can i deserialize this? (for now) serializing is not a problem.


long version

i don't know enough jackson to solve the following problem. i've tried, but i'm stuck.

the json structure i want to parse looks like this:

{
  "id": "foobar",
  "responses": [
    {
      "responseType1": {
        "code": 0,
        "foo": "bar"
      }
    },
    {
      "responseType2": {
        "code": 1,
        "bar": {"foo": ...}
      }
    },
    {
      "responseType1": {
        "code": 1,
        "foo": "foobar"
      }
    }
  ]
}

i tried to deserialize it using jacksons full data binding. my pojos are:

// pseudocode

// the outermost object
@JsonCreator
ServiceResponse(
  @JsonProperty("id") String id, 
  @JsonProperty("responses") ArrayList<ResponseWrapper> responses)

// every response has a wrapper. the wrapper is an object with just one key and one value. the value is an object of a certain class (ResponseTypeX extends AResponse), and the exact ResponseType is identified by the key (the key isn't the class name though). 
@JsonCreator
ResponseWrapper(AResponse keyDependsOnTypeOfAResponse ???)

// base class for all responseTypeX classes
// all subclasses of AResponse have a code and a complex payload object
@JsonCreator
AResponse (
  @JsonProperty("code") int code)

// one response type
// here, the payload is just a string, in reality it's a deep structure, so i dont want to parse this manually
@JsonCreator
ResponseType1 extends AResponse (
  @JsonProperty("code") int code,
  @JsonProperty("foo") String foo)

// one response type
@JsonCreator
ResponseType2 extends AResponse (
  @JsonProperty("code") int code,
  @JsonProperty("bar") SomeOtherObject foo)

as you can see, responses is an array of wrapper objects; the "payload" class of the wrapper object is identified by the key (but the keys aren't a 1:1 match to class names). my ResponseTypeX's are limited, there are about 20 of them, so if i have to do a manual key:value type identification, i'm happy.

but is it possible to write a manual deserializer for the WrapperResponse object and continue deserializing its children with full data binding? if so, how?


i tried to just make the Wrapper accept all possible ResponseTypes as properties, hoping it would just nullify the "unset" ones, e.g.

@JsonCreator
ResponseWrapper(
  @JsonProperty("responseKey1") ResponseType1 response1,
  @JsonProperty("responseKey2") ResponseType2 response2,
  @JsonProperty("responseKey3") ResponseType3 response3,
  ...)

but this failed, probably because all ResponseTypes are subclasses of AResponse and thus jackson gets confused.

Upvotes: 3

Views: 2033

Answers (1)

Programmer Bruce
Programmer Bruce

Reputation: 66935

Some custom deserialization processing is necessary. I'd recommend including a simple registry (map) of foobar/barfoo-to-type entries in the solution, much like the sixth example in my old blog post from May 25, 2011, "Deserialize JSON with Jackson into Polymorphic Types - A Complete Example".

Upvotes: 3

Related Questions