java DynamoDB JsonMarshaller 无法反序列化对象列表
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30793481/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
DynamoDB JsonMarshaller cannot Deserialize List of Object
提问by is.magl
I have a Java class which is the data-model of a table in DynamoDB. I want to use the DynamoDBMapper
to save
and load
items from Dynamo. One member of the class is a List<MyObject>
. So I used the JsonMarshaller<List<MyObject>>
to serialize and de-serialize this field.
我有一个 Java 类,它是 DynamoDB 中表的数据模型。我想使用Dynamo 中的DynamoDBMapper
tosave
和load
items。该类的一个成员是List<MyObject>
. 所以我用JsonMarshaller<List<MyObject>>
来序列化和反序列化这个字段。
The list can be successfully serialized by the JsonMarshaller
. However, when I try to retrieve the entry back and read the list, it throws an exception: java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to MyObject
. It looks like that the JsonMarshaller
de-serialize the data into the LinkedHashMap
instead of MyObject
. How can I get rid of this problem?
该列表可以通过JsonMarshaller
. 但是,当我尝试检索条目并读取列表时,它会引发异常:java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to MyObject
. 看起来JsonMarshaller
将数据反序列化为LinkedHashMap
而不是MyObject
. 我怎样才能摆脱这个问题?
The MCVE:
MCVE:
// Model.java
@DynamoDBTable(tableName = "...")
public class Model {
private String id;
private List<MyObject> objects;
public Model(String id, List<MyObject> 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<MyObject> getObjects() { return this.objects; }
public void setObjects(List<MyObject> 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<MyObject>> {}
// 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<MyObject> 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
}
}
}
采纳答案by mkobit
Part of the problem here is how the whole DynamoDB Mapper SDK deals with generics. The interface DynamoDBMarshaller<T extends Object>
has a method T unmarshall(Class<T> clazz, String obj)
, in which the class to deserialize to is passed as a parameter. The problem is that there is type erasure, and the SDK doesn't provide an easy to deal with this. Hymanson is smarter in some cases (the JsonMarshaller
uses Hymanson), which explains why the serialize
method works correctly.
这里的部分问题是整个 DynamoDB Mapper SDK 如何处理泛型。该interface DynamoDBMarshaller<T extends Object>
有一个方法T unmarshall(Class<T> clazz, String obj)
,其中,所述类反序列化到被作为参数传递。问题是存在类型擦除,SDK 没有提供易于处理的方法。Hymanson 在某些情况下更聪明(JsonMarshaller
使用 Hymanson),这就解释了为什么该serialize
方法可以正常工作。
You need to provide a better implementation for your deserialization. One way you could do this would be to implement the DynamoDBMarshaller
interface rather than extending the other one (my opinion) so you have better control over how the type is serialized.
您需要为反序列化提供更好的实现。您可以这样做的一种方法是实现DynamoDBMarshaller
接口而不是扩展另一个接口(我的意见),这样您就可以更好地控制类型的序列化方式。
Here is an example that is essentially copy/paste of the JsonMarshaller
, with minor tweaks in deserialization for the List
to give you an idea:
这是一个基本上复制/粘贴 的示例,JsonMarshaller
在反序列化中进行了细微的调整,List
以便为您提供一个想法:
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMarshaller;
import com.fasterxml.Hymanson.core.JsonProcessingException;
import com.fasterxml.Hymanson.databind.ObjectMapper;
import com.fasterxml.Hymanson.databind.ObjectWriter;
import com.fasterxml.Hymanson.databind.type.CollectionType;
import java.util.List;
import static com.amazonaws.util.Throwables.failure;
public class MyCustomMarshaller implements DynamoDBMarshaller<List<MyObject>> {
private static final ObjectMapper mapper = new ObjectMapper();
private static final ObjectWriter writer = mapper.writer();
@Override
public String marshall(List<MyObject> 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<MyObject> unmarshall(Class<List<MyObject>> 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);
}
}
}
回答by Aleris
In newer versions simply works with:
在较新的版本中只需使用:
@DynamoDBAttribute(attributeName = "things")
public List<Thing> getThings() {
return things;
}
public void setThings(final List<Thing> things) {
this.things = things;
}
given that the Thing is adnotated with:
鉴于 Thing 标有:
@DynamoDBDocument
public class Thing {
}
回答by F_SO_K
DynamoDBMarshaller is now deprecated but I get exactly the same problem with DynamoDBTypeConvertedJson. If you want to store a collection as JSON within a DynamoDBMapper class, use DynamoDBTypeConverted and write a custom converter class (do not use DynamoDBTypeConvertedJson which will not return your collection on unconvert).
DynamoDBMarshaller 现在已弃用,但我遇到了与 DynamoDBTypeConvertedJson 完全相同的问题。如果您想将集合作为 JSON 存储在 DynamoDBMapper 类中,请使用 DynamoDBTypeConverted 并编写自定义转换器类(不要使用 DynamoDBTypeConvertedJson,它不会在未转换时返回您的集合)。
Here is the solution using DynamoDBTypeConverted
这是使用 DynamoDBTypeConverted 的解决方案
// Model.java
@DynamoDBTable(tableName = "...")
public class Model {
private String id;
private List<MyObject> objects;
public Model(String id, List<MyObject> 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<MyObject> getObjects() { return this.objects; }
public void setObjects(List<MyObject> objects) { this.objects = objects; }
}
-
——
public class MyObjectConverter implements DynamoDBTypeConverter<String, List<MyObject>> {
@Override
public String convert(List<Object> objects) {
//Hymanson object mapper
ObjectMapper objectMapper = new ObjectMapper();
try {
String objectsString = objectMapper.writeValueAsString(objects);
return objectsString;
} catch (JsonProcessingException e) {
//do something
}
return null;
}
@Override
public List<Object> unconvert(String objectssString) {
ObjectMapper objectMapper = new ObjectMapper();
try {
List<Object> objects = objectMapper.readValue(objectsString, new TypeReference<List<Object>>(){});
return objects;
} catch (JsonParseException e) {
//do something
} catch (JsonMappingException e) {
//do something
} catch (IOException e) {
//do something
}
return null;
}
}
回答by David Young
I've found that the response by Aleris works fine. In my example, I have a dynamo db table that contains two collections, both of non-primitive classes.
我发现 Aleris 的响应工作正常。在我的示例中,我有一个包含两个集合的 dynamo db 表,这两个集合都是非原始类。
After trying various flavours of DBTypeConverters (taking {String, MyObject}, {Collection, Collection}, {String, Collection}) and also trying Set rather than Collection, by merely annotating the referred to class as a DynamoDBDocument, that I could pass a json array of data for those child classes and the data was persisted correctly.
在尝试了各种类型的 DBTypeConverters(采用 {String, MyObject}, {Collection, Collection}, {String, Collection})并尝试 Set 而不是 Collection 之后,只需将引用的类注释为 DynamoDBDocument,我就可以通过这些子类的 json 数据数组,并且数据已正确保存。
my "parent class" looks like this (names altered to protect the innocent);
我的“父类”看起来像这样(为了保护无辜者而改变名字);
@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<ImageRef> images;
/**
* Optional physical dimensions
*/
private Dimensions productDimensions;
/**
* Optional presentations.
*/
private Set<Presentation> presentations;
}
TypeCode is an enumeration. ImageRef, Presentation and Dimensions classes are all tagged with the DynamoDBDocument annotation.
TypeCode 是一个枚举。ImageRef、Presentation 和 Dimensions 类都使用 DynamoDBDocument 注释进行标记。
回答by Weidian Huang
Interface DynamoDBMarshaller<T extends Object>
is deprecated already, the replacement is Interface DynamoDBTypeConverter<S,T>
.
Interface DynamoDBMarshaller<T extends Object>
已弃用,替换为Interface DynamoDBTypeConverter<S,T>
.
Inside your model class, add the annotation to your list of objects.
在您的模型类中,将注释添加到您的对象列表中。
@DynamoDBTypeConverted(converter = PhoneNumberConverter.class)
public List<MyObject> getObjects() { return this.objects; }
public void setObjects(List objects) { this.objects = objects; }
public void setObjects(List objects) { this.objects = objects; }
And this is the implementation of DynamoDBTypeConverter
.
这是DynamoDBTypeConverter
.
public class PhoneNumberConverterimplements DynamoDBTypeConverter<String, PhoneNumber>
{
private static final ObjectMapper mapper = new ObjectMapper();
private static final ObjectWriter writer = mapper.writerWithType(new TypeReference<List<MyObject>>(){});
@Override
public String convert(List<MyObject> 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<MyObject> unconvert(String s) {
TypeReference<List<MyObject>> type = new TypeReference<List<MyObject>>() {};
try {
List<MyObject> list = mapper.readValue(s, type);
return list;
} catch (Exception e) {
System.out.println("Unable to unmarshall the string " + s
+ "into " + s);
return null;
}
}
}