Skip to content

Converter called twice results in ClassCastException #1189

@carrino

Description

@carrino

I have a minimal repro below. testConverter fails with a class cast. The converter is called once on JsonNode and returns a SubClass. Then it is called again with that returned SubClass and fails.

testConverter2 passes.

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.util.StdConverter;
import java.util.Collections;
import org.junit.Assert;
import org.junit.Test;

public class JacksonConverterTest {
    @JsonSubTypes({
        @JsonSubTypes.Type(SubClass.class),
    })
    @JsonTypeInfo(use = Id.NAME, include = As.EXISTING_PROPERTY, property = "type", visible = false)
    interface Base {
        String getType();
    }

    @JsonTypeName("sub")
    @JsonSerialize(as = ImmutableSubClass.class)
    @JsonDeserialize(as = ImmutableSubClass.class)
    interface SubClass extends Base {
        @Override
        default String getType() {
            return "sub";
        }

        default boolean getVisible() {
            return true;
        }

    }

    static class ImmutableSubClass implements SubClass {
        public boolean visible = true;

        @Override
        public int hashCode() {
            return visible ? 0 : 1;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            ImmutableSubClass other = (ImmutableSubClass) obj;
            if (visible != other.visible)
                return false;
            return true;
        }
    }

    @Test
    public void testConverter() throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        ObjectNode json = new ObjectNode(JsonNodeFactory.instance);
        json.put("hidden", false);
        json.put("type", "sub");

        mapper.setMixIns(Collections.singletonMap(SubClass.class, SubClassMixin.class));

        SubClass value = mapper.treeToValue(json, SubClass.class);
        Assert.assertEquals(new ImmutableSubClass(), value);
    }

    @Test
    public void testConverter2() throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        ObjectNode json = new ObjectNode(JsonNodeFactory.instance);
        json.put("hidden", false);
        json.put("type", "sub");

        mapper.setMixIns(Collections.singletonMap(SubClass.class, SubClassMixin.class));

        Base value = mapper.treeToValue(json, Base.class);
        Assert.assertEquals(new ImmutableSubClass(), value);
    }

    @JsonDeserialize(converter = SubClassConverter.class)
    interface SubClassMixin {}

    static class SubClassConverter extends StdConverter<JsonNode, SubClass> {
        @Override
        public SubClass convert(JsonNode value) {
            // Modify the old Json in some way that the new library will accept.
            ObjectNode node = (ObjectNode) value;
            boolean hidden = node.remove("hidden").asBoolean(false);
            node.put("visible", !hidden);
            node.put("type", "sub");
            try {
                return new ObjectMapper().treeToValue(node, SubClass.class);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions