DynamoDB JsonMarshaller无法反序列化对象列表
我有一个Java类,它是DynamoDB中表的数据模型。 我想使用DynamoDBMapper
来save
和load
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
,其中要反序列化的类作为参数传递。 问题是存在类型擦除 ,并且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
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注释进行标记。