Unmarshalling DynamoDB Map<String, AttributeValue> to document-style JSON in AWS Java SDK 2.x

3.1k Views Asked by At

The answers to the question Converting DynamoDB JSON to Standard JSON with Java are obsolete in 2022.

The problem is that both of these imports no longer work:

import com.amazonaws.services.dynamodbv2.document.internal.InternalUtils;

import com.amazonaws.services.dynamodbv2.document.ItemUtils;

What happens: the import fails.

You can also verify it in the documentation, here: https://sdk.amazonaws.com/java/api/latest/index.html?software/amazon/awssdk/services/dynamodb/package-summary.html

The question is, what classes replace these two classes in SDK 2.x?

1

There are 1 best solutions below

0
On BEST ANSWER

Per the comments above, there is currently no solution via the AWS SDK V2.

The following code works to convert values to usable JSON when working directly with the DynamoDB API:

private Object unwrapAttributeValue(AttributeValue av) {
    if (av.isNULL() != null && av.isNULL())
        return null;
    if (av.getM() != null)
        return unmarshall(av.getM());
    if (av.getL() != null)
        return av.getL().stream().map(this::unwrapAttributeValue)
                .collect(Collectors.toList());
    return Stream
            .<Function<AttributeValue, Object>>of(AttributeValue::getS,
                    AttributeValue::getN, AttributeValue::getBOOL,
                    AttributeValue::getNS, AttributeValue::getSS,
                    AttributeValue::getB, AttributeValue::getBS)
            .map(f -> f.apply(av)).filter(Objects::nonNull).findFirst()
            .orElseThrow();
}
public Map<String, Object> unmarshall(Map<String, AttributeValue> in) {
    Map<String, Object> out = new HashMap<>();
    for (Entry<String, AttributeValue> e : in.entrySet()) {
        Object uav = unwrapAttributeValue(e.getValue());
        if (uav != null)
            out.put(e.getKey(), uav);
    }
    return out;
}

The resulting Map<String, Object> can then be fed to Jackson for further processing like normal json data.

Another Use-Case

The following works for converting DynamoDB JSON after it's already been parsed via Jackson without going through the DynamoDB API, for example during Unit Testing with a JSON file retrieved from logs or an S3 export:

public static JsonNode unmarshall(JsonNode node) {
    String type = node.fieldNames().next();
    switch(type) {
        case "S":
        case "N":
        case "BOOL":
        case "SS":
        case "NS":
            return node.get(type);
        case "NULL":
            return null;
        case "L":
            ArrayNode arr = JsonNodeFactory.instance.arrayNode();
            node.withArray(type).forEach(item -> {
                arr.add(unmarshall(item));
            });
            return arr;
        case "M":
            ObjectNode out = JsonNodeFactory.instance.objectNode();
            node.get(type).fieldNames().forEachRemaining(key -> {
                final JsonNode value = unmarshall(node.get(type).get(key));
                out.set(key, value);
            });
            return out;
        default:
            throw new IllegalArgumentException(type);
    }
}