Skip to content

Instantly share code, notes, and snippets.

@martint
Last active August 29, 2015 13:56
Show Gist options
  • Save martint/9005047 to your computer and use it in GitHub Desktop.
Save martint/9005047 to your computer and use it in GitHub Desktop.

Given a JSON structure with this shape:

{ "type": "a"
  "inner": { ... } }

And the following java classes:

class Outer 
{
   private final String type;
   private final Inner inner;
   
   public Outer(String type, Inner inner)
   {
      this.type = type;
      this.inner = inner;
   }
}

interface Inner {}
class Inner1 implements Inner { ... }
class Inner2 implements Inner { ... }

How do I get Jackson to deserialize the structure into Outer, such that if type == "a", inner is created from Inner1, if it's "b" it gets created from Inner2, etc.?

BTW, I need to be able to handle the case where possible implementations of Inner are not known at compile time.

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
import com.fasterxml.jackson.databind.type.SimpleType;
import java.io.IOException;
public class TestJson
{
public static void main(String[] args)
throws IOException
{
ObjectMapper mapper = new ObjectMapper();
Outer outer = mapper.readValue("{\"type\":\"a\",\"inner\":{\"value\":\"foo\"}}", Outer.class);
}
public static class Outer
{
@JsonProperty
public String type;
@JsonProperty
@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, include= JsonTypeInfo.As.EXTERNAL_PROPERTY, property="type", visible = true)
public Inner inner;
}
@JsonTypeIdResolver(Resolver.class)
public static interface Inner
{
}
public static class Inner1
implements Inner
{
@JsonProperty
public String value;
}
public static class Inner2
implements Inner
{
@JsonProperty
public String value;
}
public static class Resolver
implements TypeIdResolver
{
@Override
public void init(JavaType baseType)
{
}
@Override
public String idFromValue(Object value)
{
if (value instanceof Inner1) {
return "a";
}
else if (value instanceof Inner2) {
return "b";
}
throw new IllegalArgumentException();
}
@Override
public String idFromValueAndType(Object value, Class<?> suggestedType)
{
return idFromValue(value);
}
@Override
public String idFromBaseType()
{
throw new UnsupportedOperationException();
}
@Override
public JavaType typeFromId(String id)
{
if (id.equals("a")) {
return SimpleType.construct(Inner1.class);
}
else if (id.equals("b")) {
return SimpleType.construct(Inner2.class);
}
throw new IllegalArgumentException();
}
@Override
public JsonTypeInfo.Id getMechanism()
{
return JsonTypeInfo.Id.CUSTOM;
}
}
}
@cowtowncoder
Copy link

I think following would do it:

class Parent 
{
   private final String type;

   @JsonTypeInfo(include=As.EXTERNAL_PROPERTY, property="type"
      // and maybe (to make 'type' property visible to code
      visible=true
   )
   private final Child child;

   public Parent(String type, Child child)
   {
      this.type = type;
      this.child = child;
   }
}

but then registration of subtypes is another question.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment