DynamoDB JsonMarshaller无法反序列化对象列表

我有一个Java类,它是DynamoDB中表的数据模型。 我想使用DynamoDBMappersaveload Dynamo中的项目。 该类的一个成员是List 。 所以我使用JsonMarshaller<List>来序列化和反序列化该字段。

该列表可以由JsonMarshaller成功序列化。 但是,当我尝试检索条目并读取列表时,它会抛出exception: java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to MyObject 。 看起来JsonMarshaller将数据反序列化为LinkedHashMap而不是MyObject 。 我该如何摆脱这个问题?

MCVE:

 // Model.java @DynamoDBTable(tableName = "...") public class Model { private String id; private List objects; public Model(String id, List objects) { this.id = id; this.objects = objects; } @DynamoDBHashKey(attributeName = "id") public String getId() { return this.id; } public void setId(String id) { this.id = id; } @DynamoDBMarshalling(marshallerClass = ObjectListMarshaller.class) public List getObjects() { return this.objects; } public void setObjects(List objects) { this.objects = objects; } } 

 // MyObject.java public class MyObject { private String name; private String property; public MyObject() { } public MyObject(String name, String property) { this.name = name; this.property = property; } public String getName() { return this.name; } public void setName(String name) { this.name = name; } public String getProperty() { return this.property; } public void setProperty(String property) { this.property = property; } } 

 // ObjectListMarshaller.java public class ObjectListMarshaller extends JsonMarshaller<List> {} 

 // Test.java public class Test { private static DynamoDBMapper mapper; static { AmazonDynamoDBClient client = new AmazonDynamoDBClient(new ProfileCredentialsProvider() mapper = new DynamoDBMapper(client); } public static void main(String[] args) { MyObject obj1 = new MyObject("name1", "property1"); MyObject obj2 = new MyObject("name2", "property2"); List objs = Arrays.asList(obj1, obj2); Model model = new Model("id1", objs); mapper.save(model); // success Model retrieved = mapper.load(Model.class, "id1"); for (MyObject obj : retrieved.getObjects()) { // exception } } } 

这里的部分问题是整个DynamoDB Mapper SDK如何处理generics。 interface DynamoDBMarshaller有一个方法T unmarshall(Class clazz, String obj) ,其中要反序列化的类作为参数传递。 问题是存在类型擦除 ,并且SDK不提供易于处理的问题。 jackson在某些情况下更聪明( JsonMarshaller使用jackson),这解释了为什么serialize方法正常工作。

您需要为反序列化提供更好的实现。 你可以做到这一点的一种方法是实现DynamoDBMarshaller接口,而不是扩展另一个(我的意见),这样你就可以更好地控制类型的序列化方式。

这是一个基本上是JsonMarshaller复制/粘贴的JsonMarshaller ,在反序列化中对List进行了一些小的调整,以便给出一个想法:

 import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMarshaller; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.type.CollectionType; import java.util.List; import static com.amazonaws.util.Throwables.failure; public class MyCustomMarshaller implements DynamoDBMarshaller> { private static final ObjectMapper mapper = new ObjectMapper(); private static final ObjectWriter writer = mapper.writer(); @Override public String marshall(List obj) { try { return writer.writeValueAsString(obj); } catch (JsonProcessingException e) { throw failure(e, "Unable to marshall the instance of " + obj.getClass() + "into a string"); } } @Override public List unmarshall(Class> clazz, String json) { final CollectionType type = mapper.getTypeFactory().constructCollectionType(List.class, MyObject.class); try { return mapper.readValue(json, type); } catch (Exception e) { throw failure(e, "Unable to unmarshall the string " + json + "into " + clazz); } } } 

在v2.3.7中,它只适用于:

 @DynamoDBAttribute(attributeName = "things") public List getThings() { return things; } public void setThings(final List things) { this.things = things; } 

鉴于Thing的注释:

 @DynamoDBDocument public class Thing { } 

现在不推荐使用DynamoDBMarshaller,但我遇到了与DynamoDBTypeConvertedJson完全相同的问题。 如果要在DynamoDBMapper类中将集合存储为JSON,请使用DynamoDBTypeConverted并编写自定义转换器类(不要使用不会在非转换时返回集合的DynamoDBTypeConvertedJson)。

以下是使用DynamoDBTypeConverted的解决方案

 // Model.java @DynamoDBTable(tableName = "...") public class Model { private String id; private List objects; public Model(String id, List objects) { this.id = id; this.objects = objects; } @DynamoDBHashKey(attributeName = "id") public String getId() { return this.id; } public void setId(String id) { this.id = id; } @DynamoDBTypeConverted(converter = MyObjectConverter.class) public List getObjects() { return this.objects; } public void setObjects(List objects) { this.objects = objects; } } 

 public class MyObjectConverter implements DynamoDBTypeConverter> { @Override public String convert(List objects) { //Jackson object mapper ObjectMapper objectMapper = new ObjectMapper(); try { String objectsString = objectMapper.writeValueAsString(objects); return objectsString; } catch (JsonProcessingException e) { //do something } return null; } @Override public List unconvert(String objectssString) { ObjectMapper objectMapper = new ObjectMapper(); try { List objects = objectMapper.readValue(objectsString, new TypeReference>(){}); return objects; } catch (JsonParseException e) { //do something } catch (JsonMappingException e) { //do something } catch (IOException e) { //do something } return null; } } 

Interface DynamoDBMarshaller已弃用,替换为Interface DynamoDBTypeConverter

在模型类中,将注释添加到对象列表中。

 @DynamoDBTypeConverted(converter = PhoneNumberConverter.class) public List getObjects() { return this.objects; } 

public void setObjects(List objects){this.objects = objects; }

这是DynamoDBTypeConverter的实现。

 public class PhoneNumberConverterimplements DynamoDBTypeConverter { private static final ObjectMapper mapper = new ObjectMapper(); private static final ObjectWriter writer = mapper.writerWithType(new TypeReference>(){}); @Override public String convert(List obj) { try { return writer.writeValueAsString(obj); } catch (JsonProcessingException e) { System.out.println( "Unable to marshall the instance of " + obj.getClass() + "into a string"); return null; } } @Override public List unconvert(String s) { TypeReference> type = new TypeReference>() {}; try { List list = mapper.readValue(s, type); return list; } catch (Exception e) { System.out.println("Unable to unmarshall the string " + s + "into " + s); return null; } } } 

我发现Aleris的反应很好。 在我的例子中,我有一个dynamo db表,它包含两个非原始类的集合。

尝试各种风格的DBTypeConverters(采用{String,MyObject},{Collection,Collection},{String,Collection})并尝试Set而不是Collection时,只需将引用的类注释为DynamoDBDocument,我就可以通过这些子类的json数据数组和数据被正确保存。

我的“父类”看起来像这样(名字被改变以保护无辜者);

 @DynamoDBTable(tableName = "SomeTable") public class ParentClass { @NotNull(message = "Key must be specified") @Size(min = 12, max = 20) @DynamoDBHashKey private String key; private String type; @NotNull(message = "name must be specified.") private String name; @NotNull(message = "Type code must be specified") @DynamoDBTyped(DynamoDBMapperFieldModel.DynamoDBAttributeType.S) private TypeCode typeCode; private List images; /** * Optional physical dimensions */ private Dimensions productDimensions; /** * Optional presentations. */ private Set presentations; } 

TypeCode是枚举。 ImageRef,Presentation和Dimensions类都使用DynamoDBDocument注释进行标记。