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
提供无参数构造函数。你有两个选择。
- 使
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;
}
}
- 保留
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;
}
}
任一选项都有效。
我尝试序列化和反序列化一个对象 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
提供无参数构造函数。你有两个选择。
- 使
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;
}
}
- 保留
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;
}
}
任一选项都有效。