AWS SDK for Java の 1.x と 2.x の逆シリアル化の違い - AWS SDK for Java 2.x

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

AWS SDK for Java の 1.x と 2.x の逆シリアル化の違い

V2 の空のコレクションと V1 の nulls の比較

SDK for Java v1.x と v2.x では、空のリストとマップを使用して JSON レスポンスを逆シリアル化する方法が異なります。

SDK がリストまたはマップとしてモデル化されたプロパティが欠落したレスポンスを受信すると、V1 は欠落したプロパティを null に逆シリアル化しますが、V2 はプロパティを変更不可能な空のコレクションオブジェクトに逆シリアル化します。

たとえば、DynamoDB クライアントから describeTable メソッドに対して返されたレスポンスを考えてみましょう。次のメソッドの例には、グローバルセカンダリインデックスを持たないテーブルで describeTable メソッドを実行する V2 DynamoDB クライアントと V1 DynamoDB クライアントの両方が含まれています。

例 レスポンスで欠落している、リストとしてモデル化されたプロパティの逆シリアル化
public void deserializationDiffs(){ DescribeTableResponse v2Response = dynamoDbClientV2.describeTable(builder -> builder.tableName(TABLE_NAME)); // V2 provides has* methods on model objects for list/map members. No null check needed. LOGGER.info( String.valueOf(v2Response.table().hasGlobalSecondaryIndexes()) ); LOGGER.info( String.valueOf(v2Response.table().globalSecondaryIndexes().isEmpty()) ); // V2 deserialize to an empty collection. LOGGER.info(v2Response.table().globalSecondaryIndexes().toString()); // V1 deserialize to null. DescribeTableResult v1Result = dynamoDbClientV1.describeTable(new DescribeTableRequest(TABLE_NAME)); if (v1Result.getTable().getGlobalSecondaryIndexes() != null){ LOGGER.info(v1Result.getTable().getGlobalSecondaryIndexes().toString()); } else { LOGGER.info("The list of global secondary indexes returned by the V1 call is <null>"); } }

以下は、ログ記録された出力を示しています。

INFO org.example.DeserializationDifferences:45 - false INFO org.example.DeserializationDifferences:46 - true INFO org.example.DeserializationDifferences:48 - [] INFO org.example.DeserializationDifferences:55 - The list of global secondary indexes returned by the V1 call is <null>

Java SDK 2.x は、null を返すのではなく、空のリストとマップを変更不可能な空のコレクションに逆シリアル化することで、より安全で簡潔なコードを促進するという慣用的なアプローチを採用しています。V2 では、前の例に示した hasGlobalSecondaryIndexes のように、has* メソッドを使用してサービスがリストまたはマップとしてモデル化された属性を返したかを確認できます。

このアプローチは、明示的な null チェックが不要になるため、存在しないデータ構造や空のデータ構造を処理するための最新の Java のベストプラクティスに沿ったものとなっています。

package org.example; import com.amazonaws.regions.Regions; import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder; import com.amazonaws.services.dynamodbv2.model.DescribeTableRequest; import com.amazonaws.services.dynamodbv2.model.DescribeTableResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.model.BillingMode; import software.amazon.awssdk.services.dynamodb.model.DescribeTableResponse; import software.amazon.awssdk.services.dynamodb.model.KeyType; import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; import java.util.UUID; public class DeserializationDifferences { private static final Logger LOGGER = LoggerFactory.getLogger(DeserializationDifferences.class); private static final String TABLE_NAME = "DeserializationTable-" + UUID.randomUUID(); DynamoDbClient dynamoDbClientV2 = DynamoDbClient.create(); AmazonDynamoDB dynamoDbClientV1 = AmazonDynamoDBClientBuilder.standard().withRegion(Regions.US_EAST_1).build(); public static void main(String[] args) { DeserializationDifferences difference = new DeserializationDifferences(); difference.createTable(); difference.deserializationDiffs(); difference.deleteTable(); } public void createTable(){ dynamoDbClientV2.createTable(b -> b .billingMode(BillingMode.PAY_PER_REQUEST) .tableName(TABLE_NAME) .keySchema(b1 -> b1.attributeName("Id").keyType(KeyType.HASH)) .attributeDefinitions(b2 -> b2.attributeName("Id").attributeType(ScalarAttributeType.S))); dynamoDbClientV2.waiter().waitUntilTableExists(b -> b.tableName(TABLE_NAME)); } public void deserializationDiffs(){ DescribeTableResponse v2Response = dynamoDbClientV2.describeTable(builder -> builder.tableName(TABLE_NAME)); // V2 provides has* methods on model objects for list/map members. No null check needed. LOGGER.info( String.valueOf(v2Response.table().hasGlobalSecondaryIndexes()) ); LOGGER.info( String.valueOf(v2Response.table().globalSecondaryIndexes().isEmpty()) ); // V2 deserialize to an empty collection. LOGGER.info(v2Response.table().globalSecondaryIndexes().toString()); // V1 deserialize to null. DescribeTableResult v1Result = dynamoDbClientV1.describeTable(new DescribeTableRequest(TABLE_NAME)); if (v1Result.getTable().getGlobalSecondaryIndexes() != null){ LOGGER.info(v1Result.getTable().getGlobalSecondaryIndexes().toString()); } else { LOGGER.info("The list of global secondary indexes returned by the V1 call is <null>"); } } public void deleteTable(){ dynamoDbClientV2.deleteTable(b -> b.tableName(TABLE_NAME)); dynamoDbClientV2.waiter().waitUntilTableNotExists(b -> b.tableName(TABLE_NAME)); } }

V1 および V2 クライアントからの describeTable メソッドの JSON レスポンスには、 GlobalSecondaryIndexes 属性が含まれていません。

{ "Table": { "AttributeDefinitions": [ { "AttributeName": "Id", "AttributeType": "S" } ], "BillingModeSummary": { "BillingMode": "PAY_PER_REQUEST", "LastUpdateToPayPerRequestDateTime": ... }, "CreationDateTime": ..., "DeletionProtectionEnabled": false, "ItemCount": 0, "KeySchema": [ { "AttributeName": "Id", "KeyType": "HASH" } ], "ProvisionedThroughput": { "NumberOfDecreasesToday": 0, "ReadCapacityUnits": 0, "WriteCapacityUnits": 0 }, "TableArn": "arn:aws:dynamodb:us-east-1:11111111111:table/DeserializationTable-...", "TableId": "...", "TableName": "DeserializationTable-...", "TableSizeBytes": 0, "TableStatus": "ACTIVE", "TableThroughputModeSummary": { "LastUpdateToPayPerRequestDateTime": ..., "TableThroughputMode": "PAY_PER_REQUEST" }, "WarmThroughput": { "ReadUnitsPerSecond": 12000, "Status": "ACTIVE", "WriteUnitsPerSecond": 4000 } } }