2015-06-12 68 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註釋標記。