我想使用 AWS Java SDK 2.0 将编码为 Json 字符串的对象保存到 DynamoDB。
在 AWS Java SDK 1.n 中,可以使用
AttributeValues
将标准 Json 字符串转换为 DynamoDB Item.fromJSON(myJsonString).toAttributeValues()
。
虽然可以同时使用两个 SDK,但两个 SDK 版本(
1.11、2.0)定义的
AttributeValue
并不相同,不能互换使用。
是否有任何 AWS 提供的或社区标准的方法可以将 json 字符串/blob 转换为 AWS Java SDK 2.0 的
Map<String, AttributeValue>
?
请注意,此问题询问的是如何解决 AWS Java SDK 2.0 的问题,而不是 AWS Java SDK 1.n 的 dynamodbv2 模型。如果您认为此问题重复,请仔细检查重复问题/答案的 SDK 版本。
这有点痛苦,但这应该对人们有用:
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
public class DdbUtils {
private static final ItemParameterizedType ITEM_PARAMETERIZED_TYPE = new ItemParameterizedType();
private static final TypeReference<Map<String, AttributeValue.Builder>> ITEM_TYPEREF = new TypeReference<>() {
@Override
public Type getType() {
return ITEM_PARAMETERIZED_TYPE;
}
};
private final ObjectMapper objectMapper;
public DdbUtils() {
// This is necessary because the fields are commonly "S" or "N", but the AttributeValue builder class
// expects "s" and "n".
this(new ObjectMapper().configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true));
}
public DdbUtils(ObjectMapper objectMapper) {
this.objectMapper = Objects.requireNonNull(objectMapper);
}
public Map<String, AttributeValue> readItem(String string) throws IOException {
return buildItemMap(objectMapper.readValue(string, ITEM_TYPEREF));
}
public Map<String, AttributeValue> readItem(InputStream inputStream) throws IOException {
return buildItemMap(objectMapper.readValue(inputStream, ITEM_TYPEREF));
}
public Map<String, AttributeValue> convertItem(Object object) {
return buildItemMap(objectMapper.convertValue(object, ITEM_TYPEREF));
}
// ObjectMapper has a lot of methods; feel free to create more *Item methods right here.
private Map<String, AttributeValue> buildItemMap(Map<String, AttributeValue.Builder> map) {
return map.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().build()
));
}
// Map<String, AttributeValue.Builder>
private static class ItemParameterizedType implements ParameterizedType {
@Override
public Type getRawType() {
return Map.class;
}
@Override
public Type[] getActualTypeArguments() {
return new Type[] { String.class, AttributeValue.serializableBuilderClass() };
}
@Override
public Type getOwnerType() {
return null;
}
}
}
用途:
DdbUtils ddbUtils = new DdbUtils();
Map<String, AttributeValue> item = ddbUtils.readItem(/* whatever */);
使用 AWS Java SDK v2、Jackson 2.12 和 Java 17。
将 JSON 转换为 DynamoDB 的 AttributeValue 的示例
String productString = readFileFromResources("data/categoryFix.json");
HashMap<String,Object> result = new ObjectMapper().readValue(productString, HashMap.class);
Map<String, AttributeValue> attributes = InternalUtils.fromSimpleMap(result);
即使InternalUtils已被弃用,上述代码也运行良好。
InternalUtils
已被弃用,但下面的代码工作正常,并且还显示了如何将其转换为PutItemResult
:
HashMap responseMap = objectMapper.readValue(responseBody, HashMap.class);
Map<String, AttributeValue> attributes = InternalUtils.fromSimpleMap(responseMap);
PutItemResult result = new PutItemResult();
result.setAttributes(attributes);
这里有几种方法可以做到这一点(在 Kotlin 中),具体取决于您是使用简单的 JSON 还是 DynamoDB json:
import com.amazonaws.services.dynamodbv2.document.ItemUtils
import com.amazonaws.services.dynamodbv2.model.AttributeValue
import com.fasterxml.jackson.core.type.TypeReference
...
val objectMapper = ObjectMapper().apply {
configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
}
...
// Method 1 - simple JSON
val obj1: MutableMap<String, AttributeValue> = ItemUtils.fromSimpleMap(
objectMapper.readValue(
"""
{
"fruits": ["apple", "pear"],
"user": { "name": "Bob" }
}
""",
object : TypeReference<Map<String, Any>>() {})
)
// Method 2 - DynamoDB JSON
val obj2: Map<String, AttributeValue> =
objectMapper.readValue("""
{
"fruits": {
"L": [
{
"S": "apple"
},
{
"S": "pear"
}
]
},
"user": {
"M": {
"name": {
"S": "Bob"
}
}
}
}
""".trimIndent(),
object : TypeReference<Map<String, AttributeValue>>() {}
)