Serialize/Deserialize 一个 java.io.Serializable 对象但缺少类型 ID 属性

Serialize/Deserialize a java.io.Serializable object but missing type id property

我尝试序列化和反序列化一个对象 DataSourceObject,它包装了一个 Serializable 对象,但我不知道包装对象的类型。 当我反序列化 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 提供无参数构造函数。你有两个选择。

  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;
    }
}

任一选项都有效。