我使用Amazon的DynamoDBMapper Java类将数据保存到DynamoDB表中。该代码需要以多种不同的方式处理结构化数据,因此我希望避免编写特定于结构的代码。出于这个原因,我将代码存储为Java中的JSON对象——它们基本上是经过美化的hashmap。
我想将这些JSON对象存储到DynamoDB中,作为Dynamo相对较新的JSON文档类型。
DynamoDBMapper API的工作方式本质上是您编写一个Java类(通常是POJO),然后添加一些注释,然后将该类的对象传递给DynamoDBMapper,以便它可以将项目放入具有Java类结构的数据库中。这对于我正在做的事情的许多方面都很有效,但对于我希望这些类包含任意结构的JSON文档这一事实就不适用了。这是使用DynamoDBMapper存储JSON文档的方式,正如您所看到的,它不允许任意设置文档的结构。
我意识到我可以使用Dynamo的putItem()将json作为字符串传递到Item对象中——我只是想看看在我改变我的方法之前,我想用DynamoDBMapper做的事情是否可行。
您可以尝试使用DynamoDB Java文档SDK而不是对象映射器。这允许您使用Item类中的fromJSON和toJSON方法序列化和反序列化JSON字符串。查看http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/JavaDocumentAPIItemCRUD.html.
下面是我如何在DynamoDB中存储任意Map对象的答案。这对于归档已解组为外部对象的REST API响应非常有用。我个人使用这个来归档来自PayPal支付API的REST响应。我不关心他们在REST API中使用什么变量,也不关心他们的POJO/bean的结构。我只是想确保我保存了所有的东西。
@DynamoDBTable(tableName = "PaymentResponse")
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
@JsonSubTypes({
@JsonSubTypes.Type(value = PayPalPaymentResponse.class, name = "PayPalPaymentResponse"),
@JsonSubTypes.Type(value = BatchPayPalPaymentResponse.class, name = "BatchPayPalPaymentResponse")}
)
public abstract class PaymentResponse {
// store any arbitrary REST resrponse data in map form so we don't have to worry about the
// structure or the actual response itself
protected Map<String, String> paymentResponseData = Maps.newHashMap();
public PaymentResponse(PaymentResponseType paymentResponseType) {
this.paymentResponseType = paymentResponseType;
}
public Map<String, String> getPaymentResponseData() { return paymentResponseData; }
public void setPaymentResponseData(Map<String, String> paymentResponseData) { this.paymentResponseData = paymentResponseData; }
@Override
public String toString() {
return Arrays.toString(paymentResponseData.entrySet().toArray());
}
}
public class ConverterUtils {
public static BatchPayPalPaymentResponse getBatchPayPalPaymentResponse(PayoutBatch payoutBatch) throws IOException {
//read in the PayoutBatch response data and convert it first to a JSON string and then convert the
//JSON string into a Map<String, String>
Map<String, String> responseData = objectMapper.readValue(objectMapper.writeValueAsString(payoutBatch), new TypeReference<Map<String, String>>() {});
BatchPayPalPaymentResponse batchPayPalPaymentResponse = new BatchPayPalPaymentResponse(responseData);
return batchPayPalPaymentResponse;
}
public static PayPalPaymentResponse getSinglePayPalPaymentResponse(PayoutItemDetails payoutItemDetails) throws IOException {
//read in the paypal PayoutItemDetails response data and convert it first to a JSON string and then convert the
//JSON string into a Map<String, String>
Map<String, String> responseData = objectMapper.readValue(objectMapper.writeValueAsString(payoutItemDetails), new TypeReference<Map<String, String>>() {});
PayPalPaymentResponse payPalPaymentResponse = new PayPalPaymentResponse(responseData);
return payPalPaymentResponse;
}
}
public class BatchPayPalPaymentResponse extends PaymentResponse {
public BatchPayPalPaymentResponse(Map<String, String> responseData) {
super(responseData);
}
....
....
....
}
public class PayPalPaymentResponse extends PaymentResponse {
public PayPalPaymentResponse(Map<String, String> responseData) {
super(responseData);
}
....
....
....
}
现在你可以直接调用mapper.save(instanceOfPaymentResponse)
。请注意,我的代码还包括如何使用Jackson解析器选择要解组的PaymentResponse
的哪个子类。这是因为我使用DynamoDBTypeConverter
将我的类封送到一个字符串,然后将其放入数据库。
最后,为了完整起见,我将加入我的转换器,希望这一切都是有意义的。
public class PaymentResponseConverter implements DynamoDBTypeConverter<String, PaymentResponse> {
private static final ObjectMapper objectMapper = new ObjectMapper();
static {
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}
@Override
public String convert(PaymentResponse object) {
try {
return objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(String.format("Received invalid instance of PaymentResponse and cannot marshal it to a string (%s)", e.getMessage()));
}
}
@Override
public PaymentResponse unconvert(String object) {
try {
return objectMapper.readValue(object, PaymentResponse.class);
} catch (IOException e) {
throw new IllegalArgumentException(String.format("Unable to convert JSON to instance of PaymentResponse. This is a fatal error. (%s)", e.getMessage()));
}
}
}
我也有同样的问题,我自己把对象序列化和反序列化为json字符串,然后把它们存储为字符串。DynamoDB的整个文档概念在我看来只是一个美化了的对象序列化器。只有当你需要在dynamodb操作中访问对象内部的属性时(例如:扫描,投影)使用json文档类型是有意义的。如果我们的数据对dynamodb是不透明的,那么最好使用字符串。