0

I have a specific use case where I store the results from my one table in DynamoDB to be stored in a serialized manner in another DynamoDB. Now when I use gson to deserialize the data being retrieved, I get this error:

java.lang.RuntimeException: Unable to invoke no-args constructor for class java.nio.ByteBuffer. Register an InstanceCreator with Gson for this type may fix this problem.
        at com.google.gson.internal.ConstructorConstructor$12.construct(ConstructorConstructor.java:210)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:186)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:103)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:196)
        at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:40)
        at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:187)
        at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:145)
        at com.google.gson.Gson.fromJson(Gson.java:810)
        at com.google.gson.Gson.fromJson(Gson.java:775)

My method looks like this:

public void store(MyCustomObject obj) {
String primaryKey = obj.getKey();

List<Map<String, AttributeValue>> results = AmazonDynamoDB.query(...).getItems();
Gson gson = new Gson();
List<String>records = results .stream()
            .map(mappedResult-> gson.toJson(mappedResult))
            .collect(Collectors.toList());

Map<String, AttributeValue> attributeMap = transformToAttributeMap(records);
PutItemRequest putItemRequest = new PutItemRequest().withItem(attributeMap);
AmazonDynamoDB.putItem(...);
}

The method to retrieve the records looks something like this:

public void retrieve(String id) {
QueryRequest...
Map<String, AttributeValue> records = DynamoDB.query(...).getItems();

List<String> serializedRecords = new ArrayList<>();
List<AttributeValue> values = records.get("key");
for( AttributeValue attributeValue: values) {
     serializedRecords.add(attributeValue.getS());
}

Gson gson = new Gson();
Type recordType = new TypeToken<Map<String, AttributeValue>>() { }.getType();
List<Map<String, AttributeValue>> actualRecords = serializedRecords.stream()
                .map(record-> gson.fromJson(record, recordType))
                .collect(Collectors.toList());
}

What am I doing wrong?

DwB
  • 37,124
  • 11
  • 56
  • 82
chrisrhyno2003
  • 3,906
  • 8
  • 53
  • 102

2 Answers2

1

The problem is AttributeValue class has a field java.nio.ByteBuffer with name b. Gson tries to deserialize the data into it, but there is no default constructor for ByteBuffer class. Therefore gson cannot deserialize b field.

An alternative solution is with the new DynamoDB usage of AWS SDK. Following example should work:

AmazonDynamoDBClient client = new AmazonDynamoDBClient(
        new ProfileCredentialsProvider());
Item item = new DynamoDB(client).getTable("user").getItem("Id", "user1");
String json = item.toJSON();
Item deserialized = Item.fromJSON(json);

You should modify the credentials provider according to your setup.

mtyurt
  • 3,369
  • 6
  • 38
  • 57
0

Not exactly the best workaround/answer, but I was able to do this:

Item item = new Item().withJSON("document", jsonStr);
    Map<String,AttributeValue> attributes = InternalUtils.toAttributeValues(item);
    return attributes.get("document").getM();
chrisrhyno2003
  • 3,906
  • 8
  • 53
  • 102