ExternalTypeId2588Test.java

package com.fasterxml.jackson.databind.jsontype.ext;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.annotation.*;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;
import com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase;
import com.fasterxml.jackson.databind.testutil.DatabindTestUtil;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

// [databind#2588] / [databind#2610] / [databind#4354]
public class ExternalTypeId2588Test extends DatabindTestUtil
{
    // [databind#2588]
    interface Animal { }

    static class Cat implements Animal {
        public int lives = 9;
    }

    public static class Dog implements Animal { }

    static class Wolf implements Animal {
        public boolean alive;
    }

    @JsonIgnoreProperties(ignoreUnknown = true)
    static class Pet {
        final String type;

        @JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
                include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
                property = "type")
        @JsonTypeIdResolver(AnimalTypeIdResolver.class)
        private final Animal animal;

        @JsonCreator
        public Pet(@JsonProperty("type") String type,
                   @JsonProperty("animal") Animal animal) {
            this.type = type;
            this.animal = animal;
        }
    }

    static class AnimalTypeIdResolver extends TypeIdResolverBase {
        @Override
        public String idFromValue(Object value) {
            return idFromValueAndType(value, value.getClass());
        }

        @Override
        public String idFromValueAndType(Object value, Class<?> suggestedType) {
            if (suggestedType.isAssignableFrom(Cat.class)) {
                return "cat";
            } else if (suggestedType.isAssignableFrom(Dog.class)) {
                return "dog";
            } else if (suggestedType.isAssignableFrom(Wolf.class)) {
                return "wolf";
            }
            return null;
        }

        @Override
        public JavaType typeFromId(DatabindContext context, String id) {
            if ("cat".equals(id)) {
                return context.constructType(Cat.class);
            } else if ("dog".equals(id)) {
                return context.constructType(Dog.class);
            }
            return null;
        }

        @Override
        public JsonTypeInfo.Id getMechanism() {
            return JsonTypeInfo.Id.NAME;
        }
    }

    private final ObjectMapper MAPPER = newJsonMapper();

    // [databind#2588]
    @Test
    public void testExternalTypeId2588Read() throws Exception
    {
        Pet pet;

        // works?

        pet = MAPPER.readValue(a2q(
"{\n" +
"  'type': 'cat',\n" +
"  'animal': { },\n" +
"  'ignoredObject\": {\n" +
"    'someField': 'someValue'\n" +
"  }"+
"}"
                ), Pet.class);
        assertNotNull(pet);

        // fails:
        pet = MAPPER.readValue(a2q(
"{\n" +
"  'animal\": { },\n" +
"  'ignoredObject': {\n" +
"    'someField': 'someValue'\n" +
"  },\n" +
"  'type': 'cat'\n" +
"}"
                ), Pet.class);
        assertNotNull(pet);
    }

    @Test
    public void testExternalTypeId2588Write() throws Exception
    {
        String json = MAPPER.writeValueAsString(new Pet("cat", new Wolf()));
        assertEquals(a2q("{'animal':{'alive':false},'type':'wolf'}"), json);
    }
}