2015-06-12 15 views
6

Tôi có một lớp Java là mô hình dữ liệu của một bảng trong DynamoDB. Tôi muốn sử dụng các mục DynamoDBMapper đến saveload các mục từ Dynamo. Một thành viên của lớp học là List<MyObject>. Vì vậy, tôi đã sử dụng JsonMarshaller<List<MyObject>> để tuần tự hóa và hủy tuần tự hóa trường này.DynamoDB JsonMarshaller không thể Deserialize Danh sách đối tượng

Danh sách có thể được sắp xếp thành công theo số JsonMarshaller. Tuy nhiên, khi tôi cố gắng lấy lại mục nhập và đọc danh sách, nó ném một ngoại lệ: java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to MyObject. Dường như xóa dữ liệu JsonMarshaller thành LinkedHashMap thay vì MyObject. Làm thế nào tôi có thể loại bỏ vấn đề này?

Các 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

Làm thế nào để thợ đúc của bạn không thực hiện 'DynamoDBMarshaller'? Bạn có thể cung cấp [MCVE] (http://stackoverflow.com/help/mcve) để tạo lại ngoại lệ không? – mkobit

+0

Đã thêm MCVE. Vì '' JsonMarshaller'' đã thực thi '' DynamoDBMarshaller'', bạn không cần thực hiện lại nó. –

Trả lời

7

Một phần của vấn đề ở đây là làm thế nào toàn bộ SDK SDK của DynamoDB giao dịch với Generics. interface DynamoDBMarshaller<T extends Object> có phương thức T unmarshall(Class<T> clazz, String obj), trong đó lớp để deserialize được chuyển thành tham số. Vấn đề là có type erasure và SDK không cung cấp dễ dàng để giải quyết vấn đề này. Jackson thông minh hơn trong một số trường hợp (số JsonMarshaller sử dụng Jackson), giải thích tại sao phương thức serialize hoạt động chính xác.

Bạn cần cung cấp triển khai tốt hơn cho việc deserialization của bạn. Một cách bạn có thể thực hiện việc này là triển khai giao diện DynamoDBMarshaller thay vì mở rộng giao diện khác (ý ​​kiến ​​của tôi) để bạn có thể kiểm soát tốt hơn cách loại được sắp xếp theo thứ tự.

Dưới đây là một ví dụ mà về cơ bản được sao chép/dán của JsonMarshaller, với tinh chỉnh nhỏ trong deserialization cho List để cung cấp cho bạn một ý tưởng:

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

Tôi tạo trình soạn thảo của riêng mình trong dự án và nó hoạt động tốt. Cảm ơn. –

+0

@mkobit Điều này rất tuyệt, nhưng tôi nhận được lỗi như DynamoDBMarshaller không được chấp nhận .. – Jayendran

+0

Cảm ơn @Jayendran - Tôi không có thời gian để thực sự nhìn lại câu hỏi này nhưng tôi hy vọng cuối cùng tôi sẽ làm được! – mkobit

0

Interface DynamoDBMarshaller<T extends Object> bị phản đối đã có, việc thay thế là Interface DynamoDBTypeConverter<S,T>.

Bên trong lớp mô hình của bạn, thêm chú thích vào danh sách đối tượng của bạn.

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

public void setObjects (Danh sách đối tượng) {this.objects = objects; }

Và đây là việc triển khai 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; 
     } 
    } 
} 
4

Trong v2.3.7 nó chỉ đơn giản làm việc với:

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

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

cho rằng Thing được adnotated với:

@DynamoDBDocument 
public class Thing { 
} 
1

DynamoDBMarshaller hiện đang bị phản đối nhưng tôi nhận được chính xác cùng một vấn đề với DynamoDBTypeConvertedJson.Nếu bạn muốn lưu trữ một bộ sưu tập dưới dạng JSON trong một lớp DynamoDBMapper, hãy sử dụng DynamoDBTypeConverted và viết một lớp trình chuyển đổi tùy chỉnh (không sử dụng DynamoDBTypeConvertedJson mà sẽ không trả về bộ sưu tập của bạn khi chưa chuyển đổi).

Đây là giải pháp sử dụng 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; 
    } 
} 
0

Tôi đã phát hiện ra rằng câu trả lời của Aleris hoạt động tốt. Trong ví dụ của tôi, tôi có một bảng db máy phát điện có chứa hai bộ sưu tập, cả hai lớp không nguyên thủy.

Sau khi thử các hương vị khác nhau của DBTypeConverters (lấy {String, MyObject}, {Collection, Collection}, {String, Collection}) và cũng thử Set chứ không phải Collection, chỉ cần chú thích lớp được gọi là DynamoDBDocument, Tôi có thể truyền một mảng dữ liệu json cho các lớp con đó và dữ liệu được duy trì một cách chính xác.

"lớp cha mẹ" của tôi trông giống như thế này (tên được thay đổi để bảo vệ người vô tội);

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

Loại mã là một điều tra. Các lớp ImageRef, Presentation và Dimensions đều được gắn thẻ với chú thích DynamoDBDocument.

Các vấn đề liên quan