title: Using Jackson's @JsonTypeInfo with a custom serializer
body: <p>I'm running into a problem with Jackson where it does not respect the <code>@JsonTypeInfo</code> annotations when I use a custom serializer.  The simplified example below does not require the custom serialization and outputs the <code>type</code> property as expected when I don't use the custom serializer.  However, once the custom serializer has been enabled, the <code>type</code> property is not written and prevents deserialization.  This is a test case for my actual system, which does require a custom serializer and, if it makes a difference, is attempting to serialize a <code>Map&lt;Enum, Map&lt;Enum, T&gt;&gt;</code> where <code>T</code> is the polymorphic class whose type information is not being written.  How can I write the custom serializer so it correctly handles the type information?  I'm hoping that if I can get it working for the test case below, I will be able to apply the same concepts to the actual code.</p>
  
  <p>The test program attempts to simulate as closely as possible the serialization process I'm using in the real application, building a <code>Map&lt;&gt;</code> and serializing that instead of serializing the <code>Zoo</code> directly.</p>
  
  <p>Expected Output:</p>
  
  <pre><code>{
      "Spike": {
          "type": "dog",
          "name": "Spike",
          "breed": "mutt",
          "leashColor": "red"
      },
      "Fluffy": {
          "type": "cat",
          "name": "Fluffy",
          "favoriteToy": "spider ring"
      }
  }
  </code></pre>
  
  <p>By commenting out the custom serializer registration in the <code>SimpleModule</code> you can see the type information is output, but with the serializer registered, the output is as above but without the <code>type</code> property.</p>
  
  <pre><code>import java.io.IOException;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import org.codehaus.jackson.JsonGenerator;
  import org.codehaus.jackson.JsonProcessingException;
  import org.codehaus.jackson.Version;
  import org.codehaus.jackson.annotate.JsonCreator;
  import org.codehaus.jackson.annotate.JsonProperty;
  import org.codehaus.jackson.annotate.JsonSubTypes;
  import org.codehaus.jackson.annotate.JsonSubTypes.Type;
  import org.codehaus.jackson.annotate.JsonTypeInfo;
  import org.codehaus.jackson.annotate.JsonTypeInfo.As;
  import org.codehaus.jackson.annotate.JsonTypeInfo.Id;
  import org.codehaus.jackson.map.JsonMappingException;
  import org.codehaus.jackson.map.JsonSerializer;
  import org.codehaus.jackson.map.Module;
  import org.codehaus.jackson.map.Module.SetupContext;
  import org.codehaus.jackson.map.ObjectMapper;
  import org.codehaus.jackson.map.ResolvableSerializer;
  import org.codehaus.jackson.map.SerializationConfig.Feature;
  import org.codehaus.jackson.map.SerializerProvider;
  import org.codehaus.jackson.map.module.SimpleModule;
  import org.codehaus.jackson.map.module.SimpleSerializers;
  
  public class JacksonTest {
      public static void main(String[] args) throws Exception {
          ObjectMapper mapper = new ObjectMapper();
          Module m = new SimpleModule("TestModule", new Version(1,0,0,"")) {
              @Override
              public void setupModule(SetupContext context) {
                  super.setupModule(context);
                  context.setMixInAnnotations(Animal.class, AnimalMixIn.class);
  
                  SimpleSerializers serializers = new SimpleSerializers();
                  serializers.addSerializer(Zoo.class, new ZooSerializer());
                  context.addSerializers(serializers);
              }
          };
          mapper.registerModule(m);
          mapper.configure(Feature.INDENT_OUTPUT, true);
  
          Zoo zoo = new Zoo();
          List&lt;Animal&gt; animals = new ArrayList&lt;Animal&gt;();
          animals.add(new Dog("Spike", "mutt", "red"));
          animals.add(new Cat("Fluffy", "spider ring"));
          zoo.animals = animals;
  
          System.out.println(zoo);
          String json = mapper.writeValueAsString(zoo);
          System.out.println(json);
      }
  
      static class Zoo {
          public Collection&lt;Animal&gt; animals = Collections.EMPTY_SET;
  
          public String toString() {
              StringBuilder sb = new StringBuilder();
              sb.append("Zoo: { ");
              for (Animal animal : animals)
                  sb.append(animal.toString()).append(" , ");
              return sb.toString();
          }
      }
  
      static class ZooSerializer extends JsonSerializer&lt;Zoo&gt; {
          @Override
          public void serialize(Zoo t, JsonGenerator jg, SerializerProvider sp) throws IOException, JsonProcessingException {
              Map&lt;Object, Animal&gt; animalMap = new HashMap&lt;Object, Animal&gt;();
              for (Animal a : t.animals)
                  animalMap.put(a.getName(), a);
              jg.writeObject(animalMap);
          }
      }
  
      @JsonTypeInfo(
              use=Id.NAME,
              include=As.PROPERTY,
              property="type")
      @JsonSubTypes({
          @Type(value=Cat.class,name="cat"),
          @Type(value=Dog.class,name="dog")
      })
      static abstract class AnimalMixIn {
      }
  
      static interface Animal&lt;T&gt; {
          T getName();
      }
  
      static abstract class AbstractAnimal&lt;T&gt; implements Animal&lt;T&gt; {
          private final T name;
  
          protected AbstractAnimal(T name) {
              this.name = name;
          }
  
          public T getName() {
              return name;
          }
      }
  
      static class Dog extends AbstractAnimal&lt;String&gt; {
          private final String breed;
          private final String leashColor;
  
          @JsonCreator
          public Dog(@JsonProperty("name") String name, @JsonProperty("breed") String breed,
                     @JsonProperty("leashColor") String leashColor)
          {
              super(name);
              this.breed = breed;
              this.leashColor = leashColor;
          }
  
          public String getBreed() {
              return breed;
          }
  
          public String getLeashColor() {
              return leashColor;
          }
  
          @Override
          public String toString() {
              return "Dog{" + "name=" + getName() + ", breed=" + breed + ", leashColor=" + leashColor + "}";
          }
      }
  
      static class Cat extends AbstractAnimal&lt;String&gt; {
          private final String favoriteToy;
  
          @JsonCreator
          public Cat(@JsonProperty("name") String name, @JsonProperty("favoriteToy") String favoriteToy) {
              super(name);
              this.favoriteToy = favoriteToy;
          }
  
          public String getFavoriteToy() {
              return favoriteToy;
          }
  
          @Override
          public String toString() {
              return "Cat{" + "name=" + getName() + ", favoriteToy=" + favoriteToy + '}';
          }
      }
  }
  </code></pre>
  
  <h2>EDIT: Adding additional test cases that may clarify the problem</h2>
  
  <p>After reading some more questions about similar issues, I decided to try modifying my custom serializer to narrow down where the problem is.  I discovered that adding my <code>Animal</code> objects to any generically typed Collection (<code>List&lt;Animal&gt;</code> and <code>Map&lt;Object, Animal&gt;</code> were tested), the type information was not serialized.  However, when serializing an <code>Animal[]</code>, the type information was included.  Unfortunately, while I can vary that behavior in the test code, I need my production code to serialize a <code>Map</code> with polymorphic values.</p>
  
  <p>Changing the custom <code>ZooSerializer.serialize()</code> method to the following does output type information, but loses the <code>Map</code> semantics I need:</p>
  
  <pre><code>public void serialize(...) {
      Animal[] animals = t.animals.toArray(new Animal[0]);
      jg.writeObject(animals);
  }
  </code></pre>