2015-06-12 42 views
6

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

该列表可以通过JsonMarshaller成功序列化。但是,当我尝试检索条目并读取列表时,它会引发异常: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<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 
    } 
    } 
} 
+0

youryour marshaller怎么没有实现'DynamoDBMarshaller'?你能提供[MCVE](http://stackoverflow.com/help/mcve)来重现异常吗? – mkobit

+0

添加了MCVE。由于''JsonMarshaller''实现了''DynamoDBMarshaller'',所以你不需要再次实现它。 –

回答

7

的部分问题是在这里整个DynamoDB Mapper SDK如何处理泛型。 interface DynamoDBMarshaller<T extends Object>有一个方法T unmarshall(Class<T> clazz, String obj),其中要反序列化的类作为参数传递。问题是有type erasure,并且SDK不提供一个容易处理这个。杰克逊在某些情况下更为智能(JsonMarshaller使用杰克逊),这就解释了为什么serialize方法能正常工作。

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

这里有一个例子是基本上复制/ 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<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); 
     } 
    } 
} 
+0

我在项目中创建了我自己的编组器,它运行良好。谢谢。 –

+0

@mkobit这很好,但我得到的错误作为DynamoDBMarshaller已弃用.. – Jayendran

+0

谢谢@Jayendran - 我没有时间真正回顾这个问题,但我希望我最终会! – mkobit

0

Interface DynamoDBMarshaller<T extends Object>已经过时,更换为Interface DynamoDBTypeConverter<S,T>

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

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

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

而这是DynamoDBTypeConverter的执行。

@DynamoDBAttribute(attributeName = "things") 
public List<Thing> getThings() { 
    return things; 
} 

public void setThings(final List<Thing> things) { 
    this.things = things; 
} 

考虑到的事情是adnotated有:

@DynamoDBDocument 
public class Thing { 
} 
4

在v2.3.7它简单地用作品DynamoDBTypeConvertedJson。如果要将集合作为JSON存储在DynamoDBMapper类中,请使用DynamoDBTypeConverted并编写一个自定义转换器类(不要使用DynamoDBTypeConvertedJson,它将不会以非转换方式返回您的集合)。

下面是使用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) { 
     //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<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; 
    } 
} 
1

DynamoDBMarshaller现在已经过时,但我得到完全相同的问题

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

我发现,通过爱励响应工作正常。在我的例子中,我有一个Dynamo数据库表,其中包含两个集合,都是非原始类。

尝试使用各种风格的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<ImageRef> images; 

    /** 
    * Optional physical dimensions 
    */ 
    private Dimensions productDimensions; 

    /** 
    * Optional presentations. 
    */ 
    private Set<Presentation> presentations; 
} 

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