package com.bazaarvoice.jackson; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.MappingJsonFactory; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.guava.GuavaModule; import com.google.common.base.Objects; import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; import java.util.Map; public class Bug { static class ContainerA { @JsonProperty private Optional name = Optional.absent(); @JsonProperty private Optional strategy = Optional.absent(); @Override public String toString() { return Objects.toStringHelper(this) .add("name", name) .add("strategy", strategy) .toString(); } } static class ContainerB { @JsonProperty private Optional name = Optional.absent(); @JsonProperty private Strategy strategy = null; @Override public String toString() { return Objects.toStringHelper(this) .add("name", name) .add("strategy", strategy) .toString(); } } @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") @JsonSubTypes({ @JsonSubTypes.Type(name = "Foo", value = Foo.class), @JsonSubTypes.Type(name = "Bar", value = Bar.class), @JsonSubTypes.Type(name = "Baz", value = Baz.class) }) static interface Strategy { } static class Foo implements Strategy { @JsonProperty private final int foo; @JsonCreator Foo(@JsonProperty("foo") int foo) { this.foo = foo; } @Override public String toString() { return Objects.toStringHelper(this) .add("foo", foo) .toString(); } } static class Bar implements Strategy { @JsonProperty private final boolean bar; @JsonCreator Bar(@JsonProperty("bar") boolean bar) { this.bar = bar; } @Override public String toString() { return Objects.toStringHelper(this) .add("bar", bar) .toString(); } } static class Baz implements Strategy { @JsonProperty private final String baz; @JsonCreator Baz(@JsonProperty("baz") String baz) { this.baz = baz; } @Override public String toString() { return Objects.toStringHelper(this) .add("baz", baz) .toString(); } } private static final ObjectMapper mapper = new MappingJsonFactory().getCodec().registerModule(new GuavaModule()); public static void main(String[] args) throws JsonProcessingException { ImmutableMap foo = ImmutableMap.builder() .put("name", "foo strategy") .put("strategy", ImmutableMap.builder() .put("type", "Foo") .put("foo", 42) .build()) .build(); ImmutableMap bar = ImmutableMap.builder() .put("name", "bar strategy") .put("strategy", ImmutableMap.builder() .put("type", "Boo") .put("bar", true) .build()) .build(); ImmutableMap baz = ImmutableMap.builder() .put("name", "baz strategy") .put("strategy", ImmutableMap.builder() .put("type", "Baz") .put("baz", "hello world!") .build()) .build(); test(foo); test(bar); test(baz); } private static void test(Map map) throws JsonProcessingException { String json = mapper.writeValueAsString(map); System.out.println("Running test case on: " + json); System.out.println(); System.out.println("Trying ContainerA:"); try { ContainerA obj = mapper.readValue(json, ContainerA.class); System.out.println(obj); } catch (Exception e) { e.printStackTrace(System.out); } System.out.println(); System.out.println("Trying ContainerB:"); try { ContainerB obj = mapper.readValue(json, ContainerB.class); System.out.println(obj); } catch (Exception e) { e.printStackTrace(System.out); } System.out.println(); System.out.println(); System.out.println("==============================="); System.out.println(); } }