【问题标题】:Serialize/Deserialize a java.io.Serializable object but missing type id property序列化/反序列化 java.io.Serializable 对象但缺少类型 id 属性
【发布时间】:2021-12-13 11:49:25
【问题描述】:

我尝试序列化和反序列化包装 Serializable 对象的对象 DataSourceObject,但我不知道被包装对象的类型。 当我反序列化 JSON 时,出现异常:

Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.accor.assets.TestSerialization$DataSourceObject` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (String)"{"@class":"com.accor.assets.TestSerialization$DataSourceObject","concreteObject":{"@class":"com.accor.assets.TestSerialization$HotelsListBean","version":"1.0","metaResponse":{"@class":"com.accor.assets.TestSerialization$MetaResponse","returncode":"0","date":"10/28/21 09:39:14 AM"},"hotelsList":{"@class":"com.accor.assets.TestSerialization$HotelsList","hotel":["java.util.ArrayList",[{"@class":"com.accor.assets.TestSerialization$Hotel","name":"My Hotel","code":"H001","nbstars":"4","countryCode":"G"[truncated 8 chars]; line: 1, column: 65]
    at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
    at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1764)
    at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400)
    at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1209)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1415)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:362)

这里是重现问题的完整示例:

public class TestSerialization {

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper objectMapper = objectMapper();

        HotelsListBean hotelsListBean = new HotelsListBean();

        hotelsListBean.setVersion("1.0");
        MetaResponse metaResponse = new MetaResponse();
        metaResponse.setReturncode("0");
        metaResponse.setDate("10/28/21 09:39:14 AM");
        hotelsListBean.setMetaResponse(metaResponse);

        HotelsList hotelsList = new HotelsList();
        Hotel hotel = new Hotel();
        hotel.setCode("H001");
        hotel.setName("My Hotel");
        hotel.setCountryCode("GB");
        hotel.setNbstars("4");
        hotelsList.getHotel().add(hotel);
        hotelsListBean.setHotelsList(hotelsList);

        DataSourceObject<HotelsListBean> dataSourceObject = new DataSourceObject<>(hotelsListBean);

        String json = objectMapper.writeValueAsString(dataSourceObject);
        System.out.println(json);

        Object result = objectMapper.readValue(json, Object.class);
        System.out.println(result);
    }

    private static ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();

        objectMapper.enable(JsonGenerator.Feature.IGNORE_UNKNOWN);
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
        objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);

        // Register support of other new Java 8 datatypes outside of date/time: most notably Optional, OptionalLong, OptionalDouble
        objectMapper.registerModule(new Jdk8Module());

        // Register support for Java 8 date/time types (specified in JSR-310 specification)
        objectMapper.registerModule(new JavaTimeModule());

        return objectMapper;
    }

    public static class DataSourceObject<T extends Serializable> implements Serializable {

        private static final long serialVersionUID = -6026669040755678830L;

        private final T concreteObject;

        public DataSourceObject(final T concreteObject) {
            this.concreteObject = concreteObject;
        }

        public T getConcreteObject() {
            return concreteObject;
        }

    }

    public static class HotelsListBean implements Serializable {

        private final static long serialVersionUID = 1L;

        protected String version;

        protected MetaResponse metaResponse;

        protected HotelsList hotelsList;

        public String getVersion() {
            return version;
        }

        public void setVersion(String value) {
            this.version = value;
        }

        public MetaResponse getMetaResponse() {
            return metaResponse;
        }

        public void setMetaResponse(MetaResponse value) {
            this.metaResponse = value;
        }

        public HotelsList getHotelsList() {
            return hotelsList;
        }

        public void setHotelsList(HotelsList hotelsList) {
            this.hotelsList = hotelsList;
        }
    }

    public static class MetaResponse implements Serializable {

        private final static long serialVersionUID = 1L;

        protected String returncode;
        protected String date;

        public String getReturncode() {
            return returncode;
        }

        public void setReturncode(String value) {
            this.returncode = value;
        }

        public String getDate() {
            return date;
        }

        public void setDate(String value) {
            this.date = value;
        }
    }

    public static class HotelsList implements Serializable {

        private final static long serialVersionUID = 1L;

        protected List<Hotel> hotel;

        public List<Hotel> getHotel() {
            if (hotel == null) {
                hotel = new ArrayList<Hotel>();
            }
            return this.hotel;
        }
    }

    public static class Hotel implements Serializable {

        private final static long serialVersionUID = 1L;

        protected String name;
        protected String code;
        protected String nbstars;
        protected String countryCode;

        public String getName() {
            return name;
        }

        public void setName(String value) {
            this.name = value;
        }

        public String getCode() {
            return code;
        }

        public void setCode(String value) {
            this.code = value;
        }

        public String getNbstars() {
            return nbstars;
        }

        public void setNbstars(String value) {
            this.nbstars = value;
        }

        public String getCountryCode() {
            return countryCode;
        }

        public void setCountryCode(String value) {
            this.countryCode = value;
        }
    }
}

我想知道什么可以成功反序列化。

(我试图在 DataSourceObject 构造函数中添加@JsonCreator,但我得到了同样的异常)

【问题讨论】:

  • 我们可以在DataSourceObject构造函数上使用@JsonCreator,但是我们必须在构造函数的参数中添加@JsonProperty

标签: java json serialization jackson deserialization


【解决方案1】:

您需要为DataSourceObject 提供一个无参数构造函数。你有两个选择。

  1. 使concreteObject 不是最终的:
public static class DataSourceObject<T extends Serializable> implements Serializable {

    private static final long serialVersionUID = -6026669040755678830L;

    private T concreteObject;

    private DataSourceObject() { }

    public DataSourceObject(final T concreteObject) {
        this.concreteObject = concreteObject;
    }

    public T getConcreteObject() {
        return concreteObject;
    }
}
  1. 保留concreteObject final,但必须在无参数构造函数中将其分配给null
public static class DataSourceObject<T extends Serializable> implements Serializable {

    private static final long serialVersionUID = -6026669040755678830L;

    private final T concreteObject;

    private DataSourceObject() {
        concreteObject = null;
    }

    public DataSourceObject(final T concreteObject) {
        this.concreteObject = concreteObject;
    }

    public T getConcreteObject() {
        return concreteObject;
    }
}

任何一个选项都可以。

【讨论】:

  • 感谢您的回答。我使用默认的私有构造函数进行了测试,它也可以工作。如果我们不想公开默认构造函数,这会很有用。
  • 如果不能使用私有构造函数,必须定义可见性:objectMapper.setVisibility(PropertyAccessor.CREATOR, JsonAutoDetect.Visibility.ANY);objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
  • 好点,我会将构造函数设为私有 ;)
猜你喜欢
  • 1970-01-01
  • 2020-10-18
  • 1970-01-01
  • 2015-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多