【问题标题】:@JsonTypeInfo not adding "@class" id to json when wrapping class also has @JsonTypeInfo@JsonTypeInfo 不添加“@class” id 到 json 时包装类也有 @JsonTypeInfo
【发布时间】:2017-01-21 00:58:17
【问题描述】:

我正在尝试序列化一个“持有者”类,该类中有一个类型派生自泛型的值。即“class HasValue”里面有一个“私有A值”。

此外,我还有接口“Inf”和实现该接口的具体类“InfCls”。该接口标有@JsonTypeInfo,并被告知创建一个“@class”字段。

如果持有者类上没有@JsonTypeInfo,一切正常。但是当我添加它时它会中断。

注意:虽然我在这个实例中不需要 @JsonTypeInfo(因为持有者总是一个具体的类),但在需要它时也会出现同样的问题,所以我选择这个作为一个更简单的例子。

SCWE 下面:

import static org.junit.Assert.assertEquals;

import java.util.Objects;

import org.junit.Test;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;

public class SerializationIssues {

    // No @JsonTypeInfo on HasValue
    @Test
    public void json_HasValue_Inf_javaType() throws Exception {
        HasValue<Inf> object = new HasValue<>(new InfCls(1));

        ObjectMapper objectMapper = new ObjectMapper();
        JavaType javaType = objectMapper.getDeserializationConfig().getTypeFactory().constructParametricType(HasValue.class, Inf.class);
        String serialized = objectMapper.writerFor(javaType).writeValueAsString(object);
        HasValue<?> deserialized = objectMapper.readValue(serialized, javaType);

        assertEquals(object, deserialized);
    }

    // @JsonTypeInfo on HasValueId
    @Test
    public void json_HasValueId_Inf_javaType() throws Exception {
        HasValueId<Inf> object = new HasValueId<>(new InfCls(1));

        ObjectMapper objectMapper = new ObjectMapper();
        JavaType javaType = objectMapper.getDeserializationConfig().getTypeFactory().constructParametricType(HasValueId.class, Inf.class);
        String serialized = objectMapper.writerFor(javaType).writeValueAsString(object);
        HasValueId<?> deserialized = objectMapper.readValue(serialized, javaType);

        assertEquals(object, deserialized);
    }

    // ===== An interface and impl to test @JsonTypeInfo =====
    @JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS, include = As.PROPERTY, property = "@class")
    public static interface Inf {
    }

    @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
    public static class InfCls implements Inf {
        private int val;

        @JsonCreator
        public InfCls(@JsonProperty("val") int val) {
            this.val = val;
        }

        @Override
        public String toString() {
            return "InfCls [val=" + val + "]";
        }

        @Override
        public int hashCode() {
            return 31 * 1 + val;
        }

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

    // ===== A class that has a value =====
    @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
    public static class HasValue<A> {
        private final A val;

        @JsonCreator
        public HasValue(@JsonProperty("val") A val) {
            this.val = Objects.requireNonNull(val);
        }

        public A get() {
            return val;
        }

        @Override
        public String toString() {
            return "HasValue [val=" + val + "]";
        }

        @Override
        public int hashCode() {
            return 31 * 1 + ((val == null) ? 0 : val.hashCode());
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            HasValue<?> other = (HasValue<?>) obj;
            if (val == null) {
                if (other.val != null)
                    return false;
            } else if (!val.equals(other.val))
                return false;
            return true;
        }
    }

    @JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
    @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
    public static class HasValueId<A> {
        private final A val;

        @JsonCreator
        public HasValueId(@JsonProperty("val") A val) {
            this.val = Objects.requireNonNull(val);
        }

        public A get() {
            return val;
        }

        @Override
        public String toString() {
            return "HasValue [val=" + val + "]";
        }

        @Override
        public int hashCode() {
            return 31 * 1 + ((val == null) ? 0 : val.hashCode());
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            HasValueId<?> other = (HasValueId<?>) obj;
            if (val == null) {
                if (other.val != null)
                    return false;
            } else if (!val.equals(other.val))
                return false;
            return true;
        }
    }

}

【问题讨论】:

    标签: java json serialization jackson


    【解决方案1】:

    一旦遇到@JsonTypeInfo 注释,您正在引导的序列化程序就会被推开,并且您提供的类型信息会丢失。

    有几种方法可以解决此问题。您可以在类中提供明确的类型信息:

    public static class HasValueId<A extends Inf> {
        private final A val;
    

    这将允许杰克逊通过自省来确定字段类型并找到您的第二个注释。

    或者您可以使用 @JsonTypeInfo 注释通用字段:

    public static class HasValueId<A> {
        @JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS, include = As.PROPERTY, property = "@class")
        private final A val; 
    

    ...这将消除对接口注释的需要。

    完成此操作后,您将不再需要键入您的作者;普通的旧 objectMapper.writeValueAsString(object); 可以工作。

    【讨论】:

    • 这确实解决了我的问题。感谢您的快速回复。关于何时“推出”序列化程序有什么一般规则吗?
    • 抱歉术语模糊,但我不知道官方语言是什么。如果您查看ObjectWriter.Prefetch.forRootType,您会发现有问题的代码。一旦找到多态类型,它就会“解包”序列化程序,大概是为了让动态类型优先于静态类型。
    猜你喜欢
    • 2014-03-30
    • 2012-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-12
    • 2014-05-03
    相关资源
    最近更新 更多