

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

# DynamoDB 用の拡張ドキュメント API を使用して JSON ドキュメントを操作する
<a name="ddb-en-client-doc-api"></a>

の[拡張ドキュメント API](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/package-summary.html) AWS SDK for Java 2.x は、固定スキーマを持たないドキュメント指向のデータで動作するように設計されています。ただし、カスタムクラスを使用して個々の属性をマップすることもできます。

 拡張ドキュメント API は、v1.x の[ドキュメント API](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/document/DynamoDB.html) AWS SDK for Java の後継です。

**Contents**
+ [拡張ドキュメント API の使用を開始する](ddb-en-client-doc-api-steps.md)
  + [`DocumentTableSchema` および `DynamoDbTable` を作成する](ddb-en-client-doc-api-steps.md#ddb-en-client-doc-api-steps-createschema)
+ [拡張ドキュメントを構築する](ddb-en-client-doc-api-steps-create-ed.md)
  + [JSON 文字列から構築する](ddb-en-client-doc-api-steps-create-ed.md#ddb-en-client-doc-api-steps-create-ed-fromJson)
  + [個々の要素から構築する](ddb-en-client-doc-api-steps-create-ed.md#ddb-en-client-doc-api-steps-create-ed-fromparts)
+ [CRUD オペレーションを実行する](ddb-en-client-doc-api-steps-use.md)
+ [拡張ドキュメント属性にはカスタムオブジェクトとしてアクセスできます。](ddb-en-client-doc-api-convert.md)
+ [DynamoDB を使用せずに `EnhancedDocument` を使用する](ddb-en-client-doc-api-standalone.md)

# 拡張ドキュメント API の使用を開始する
<a name="ddb-en-client-doc-api-steps"></a>

拡張ドキュメント API には、DynamoDB 拡張クライアント API に必要なものと同じ[依存関係](ddb-en-client-getting-started.md#ddb-en-client-gs-dep)が必要です。また、このトピックの冒頭で示したように、[`DynamoDbEnhancedClient` インスタンス](ddb-en-client-getting-started-dynamodbTable.md#ddb-en-client-getting-started-dynamodbTable-eclient)も必要です。

Enhanced Document API は のバージョン 2.20.3 でリリースされたため AWS SDK for Java 2.x、そのバージョン以降が必要です。

## `DocumentTableSchema` および `DynamoDbTable` を作成する
<a name="ddb-en-client-doc-api-steps-createschema"></a>

拡張ドキュメント API を使用して DynamoDB テーブルに対してコマンドを呼び出すには、テーブルをクライアント側の [DynamoDbTable<EnhancedDocument>](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html) リソースオブジェクトに関連付けます。

拡張クライアントの `table()` メソッドは `DynamoDbTable<EnhancedDocument>` インスタンスを作成し、DynamoDB テーブル名と `DocumentTableSchema` のパラメータを必要とします。

[DocumentTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/DocumentTableSchema.html) のビルダーには、プライマリインデックスキーと 1 つ以上の属性コンバータープロバイダーが必要です。この `AttributeConverterProvider.defaultProvider()` メソッドは[デフォルトタイプ](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/package-summary.html)のコンバーターを提供します。カスタム属性コンバータープロバイダーを提供する場合でも、指定する必要があります。オプションのセカンダリインデックスキーをビルダーに追加できます。

次のコードスニペットは、スキーマレス `person` オブジェクトを格納する DynamoDB `EnhancedDocument` テーブルのクライアント側表現を生成するコードを示しています。

```
DynamoDbTable<EnhancedDocument> documentDynamoDbTable = 
                enhancedClient.table("person",
                        TableSchema.documentSchemaBuilder()
                            // Specify the primary key attributes.
                            .addIndexPartitionKey(TableMetadata.primaryIndexName(),"id", AttributeValueType.S)
                            .addIndexSortKey(TableMetadata.primaryIndexName(), "lastName", AttributeValueType.S)
                            // Specify attribute converter providers. Minimally add the default one.
                            .attributeConverterProviders(AttributeConverterProvider.defaultProvider())
                            .build());
                                                         
// Call documentTable.createTable() if "person" does not exist in DynamoDB.
// createTable() should be called only one time.
```

このセクション全体で使用される `person` オブジェクトの JSON 表現を以下に示します。

### JSON `person` オブジェクト
<a name="ddb-en-client-doc-api-steps-createschema-obj"></a>

```
{
  "id": 1,
  "firstName": "Richard",
  "lastName": "Roe",
  "age": 25,
  "addresses":
    {
      "home": {
        "zipCode": "00000",
        "city": "Any Town",
        "state": "FL",
        "street": "123 Any Street"
      },
      "work": {
        "zipCode": "00001",
        "city": "Anywhere",
        "state": "FL",
        "street": "100 Main Street"
      }
    },
  "hobbies": [
    "Hobby 1",
    "Hobby 2"
  ],
  "phoneNumbers": [
    {
      "type": "Home",
      "number": "555-0100"
    },
    {
      "type": "Work",
      "number": "555-0119"
    }
  ]
}
```

# 拡張ドキュメントを構築する
<a name="ddb-en-client-doc-api-steps-create-ed"></a>

`[EnhancedDocument](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/EnhancedDocument.html)` は、ネストされた属性を持ち、複雑な構造を持つドキュメントタイプのオブジェクトを表します。`EnhancedDocument` には、`DocumentTableSchema` に指定されたプライマリキー属性と一致する最上位の属性が必要です。残りの内容は任意で、最上位の属性だけでなく、深くネストされた属性で構成することもできます。

`EnhancedDocument` インスタンスを作成するには、複数の方法で要素を追加できるビルダーを使用します。

## JSON 文字列から構築する
<a name="ddb-en-client-doc-api-steps-create-ed-fromJson"></a>

JSON 文字列を使用して、1 つのメソッド呼び出しに `EnhancedDocument` を構築できます。次のスニペットは、`jsonPerson()` ヘルパーメソッドから返された JSON 文字列から `EnhancedDocument` を作成します。この `jsonPerson()` メソッドは、前に示した [person オブジェクト](ddb-en-client-doc-api-steps.md#ddb-en-client-doc-api-steps-createschema-obj)の JSON 文字列バージョンを返します。

```
EnhancedDocument document = 
        EnhancedDocument.builder()
                        .json( jsonPerson() )
                        .build());
```

## 個々の要素から構築する
<a name="ddb-en-client-doc-api-steps-create-ed-fromparts"></a>

また、ビルダーのタイプセーフメソッドを使用して個々のコンポーネントから `EnhancedDocument` インスタンスを構築することもできます。

次の例では、前の例の JSON 文字列から作成された拡張ドキュメントと同様の `person` 拡張ドキュメントを作成します。

```
        /* Define the shape of an address map whose JSON representation looks like the following.
           Use 'addressMapEnhancedType' in the following EnhancedDocument.builder() to simplify the code.
           "home": {
             "zipCode": "00000",
             "city": "Any Town",
             "state": "FL",
             "street": "123 Any Street"
           }*/
        EnhancedType<Map<String, String>> addressMapEnhancedType =
                EnhancedType.mapOf(EnhancedType.of(String.class), EnhancedType.of(String.class));


        //  Use the builder's typesafe methods to add elements to the enhanced document.
        EnhancedDocument personDocument = EnhancedDocument.builder()
                .putNumber("id", 50)
                .putString("firstName", "Shirley")
                .putString("lastName", "Rodriguez")
                .putNumber("age", 53)
                .putNull("nullAttribute")
                .putJson("phoneNumbers", phoneNumbersJSONString())
                /* Add the map of addresses whose JSON representation looks like the following.
                        {
                          "home": {
                            "zipCode": "00000",
                            "city": "Any Town",
                            "state": "FL",
                            "street": "123 Any Street"
                          }
                        } */
                .putMap("addresses", getAddresses(), EnhancedType.of(String.class), addressMapEnhancedType)
                .putList("hobbies", List.of("Theater", "Golf"), EnhancedType.of(String.class))
                .build();
```

### ヘルパーメソッド
<a name="ddb-en-client-doc-api-steps-use-fromparts-helpers"></a>

```
    private static String phoneNumbersJSONString() {
        return "  [" +
                "    {" +
                "      \"type\": \"Home\"," +
                "      \"number\": \"555-0140\"" +
                "    }," +
                "    {" +
                "      \"type\": \"Work\"," +
                "      \"number\": \"555-0155\"" +
                "    }" +
                "  ]";
    }

    private static Map<String, Map<String, String>> getAddresses() {
        return Map.of(
                "home", Map.of(
                        "zipCode", "00002",
                        "city", "Any Town",
                        "state", "ME",
                        "street", "123 Any Street"));

    }
```

# CRUD オペレーションを実行する
<a name="ddb-en-client-doc-api-steps-use"></a>

`EnhancedDocument` インスタンスを定義したら、DynamoDB テーブルに保存できます。次のコードスニペットでは、個々の要素から作成された [personDocument](ddb-en-client-doc-api-steps-create-ed.md#ddb-en-client-doc-api-steps-create-ed-fromparts) を使用しています。

```
documentDynamoDbTable.putItem(personDocument);
```

DynamoDB から拡張ドキュメントインスタンスを読み取った後、`personDocument` から保存されたデータにアクセスする次のコードスニペットに示すように、ゲッターを使用して個々の属性値を抽出することができます。または、サンプルコードの最後の部分に示されているように、コンテンツ全体を JSON 文字列に抽出することもできます。

```
        // Read the item.
        EnhancedDocument personDocFromDb = documentDynamoDbTable.getItem(Key.builder().partitionValue(50).build());

        // Access top-level attributes.
        logger.info("Name: {} {}", personDocFromDb.getString("firstName"), personDocFromDb.getString("lastName"));
        // Name: Shirley Rodriguez

        // Typesafe access of a deeply nested attribute. The addressMapEnhancedType shown previously defines the shape of an addresses map.
        Map<String, Map<String, String>> addresses = personDocFromDb.getMap("addresses", EnhancedType.of(String.class), addressMapEnhancedType);
        addresses.keySet().forEach(k -> logger.info(addresses.get(k).toString()));
        // {zipCode=00002, city=Any Town, street=123 Any Street, state=ME}

        // Alternatively, work with AttributeValue types checking along the way for deeply nested attributes.
        Map<String, AttributeValue> addressesMap = personDocFromDb.getMapOfUnknownType("addresses");
        addressesMap.keySet().forEach((String k) -> {
            logger.info("Looking at data for [{}] address", k);
            // Looking at data for [home] address
            AttributeValue value = addressesMap.get(k);
            AttributeValue cityValue = value.m().get("city");
            if (cityValue != null) {
                logger.info(cityValue.s());
                // Any Town
            }
        });

        List<AttributeValue> phoneNumbers = personDocFromDb.getListOfUnknownType("phoneNumbers");
        phoneNumbers.forEach((AttributeValue av) -> {
            if (av.hasM()) {
                AttributeValue type = av.m().get("type");
                if (type.s() != null) {
                    logger.info("Type of phone: {}", type.s());
                    // Type of phone: Home
                    // Type of phone: Work
                }
            }
        });

        String jsonPerson = personDocFromDb.toJson();
        logger.info(jsonPerson);
        // {"firstName":"Shirley","lastName":"Rodriguez","addresses":{"home":{"zipCode":"00002","city":"Any Town","street":"123 Any Street","state":"ME"}},"hobbies":["Theater","Golf"],
        //     "id":50,"nullAttribute":null,"age":53,"phoneNumbers":[{"number":"555-0140","type":"Home"},{"number":"555-0155","type":"Work"}]}
```

`EnhancedDocument` インスタンスは、マッピングされたデータクラスの代わりに、`[DynamoDbTable](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html)` または [DynamoDbEnhancedClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html) の任意のメソッドで使用できます。

# 拡張ドキュメント属性にはカスタムオブジェクトとしてアクセスできます。
<a name="ddb-en-client-doc-api-convert"></a>

拡張ドキュメント API では、スキーマレス構造の属性を読み書きするための API を提供するだけでなく、カスタムクラスのインスタンスとの間で属性を変換できます。

拡張ドキュメント API は、DynamoDB 拡張クライアント API の一部として[コントロール属性変換](ddb-en-client-adv-features-conversion.md)セクションに表示された `AttributeConverterProvider` と `AttributeConverter` を使用します。

次の例では、`CustomAttributeConverterProvider` をネストされた `AddressConverter` クラスとともに使用して `Address` オブジェクトを変換します。

この例は、クラスからのデータと、必要に応じて構築された構造からのデータを組み合わせることができることを示しています。この例はまた、カスタムクラスはネストされた構造のどのレベルでも使用できることを示しています。この例の `Address` オブジェクトはマップで使用される値です。

```
    public static void attributeToAddressClassMappingExample(DynamoDbEnhancedClient enhancedClient, DynamoDbClient standardClient) {
        String tableName = "customer";

        // Define the DynamoDbTable for an enhanced document.
        // The schema builder provides methods for attribute converter providers and keys.
        DynamoDbTable<EnhancedDocument> documentDynamoDbTable = enhancedClient.table(tableName,
                DocumentTableSchema.builder()
                        // Add the CustomAttributeConverterProvider along with the default when you build the table schema.
                        .attributeConverterProviders(
                                List.of(
                                        new CustomAttributeConverterProvider(),
                                        AttributeConverterProvider.defaultProvider()))
                        .addIndexPartitionKey(TableMetadata.primaryIndexName(), "id", AttributeValueType.N)
                        .addIndexSortKey(TableMetadata.primaryIndexName(), "lastName", AttributeValueType.S)
                        .build());
        // Create the DynamoDB table if needed.
        documentDynamoDbTable.createTable();
        waitForTableCreation(tableName, standardClient);


        // The getAddressesForCustomMappingExample() helper method that provides 'addresses' shows the use of a custom Address class
        // rather than using a Map<String, Map<String, String> to hold the address data.
        Map<String, Address> addresses = getAddressesForCustomMappingExample();

        // Build an EnhancedDocument instance to save an item with a mix of structures defined as needed and static classes.
        EnhancedDocument personDocument = EnhancedDocument.builder()
                .putNumber("id", 50)
                .putString("firstName", "Shirley")
                .putString("lastName", "Rodriguez")
                .putNumber("age", 53)
                .putNull("nullAttribute")
                .putJson("phoneNumbers", phoneNumbersJSONString())
                // Note the use of 'EnhancedType.of(Address.class)' instead of the more generic
                // 'EnhancedType.mapOf(EnhancedType.of(String.class), EnhancedType.of(String.class))' that was used in a previous example.
                .putMap("addresses", addresses, EnhancedType.of(String.class), EnhancedType.of(Address.class))
                .putList("hobbies", List.of("Hobby 1", "Hobby 2"), EnhancedType.of(String.class))
                .build();
        // Save the item to DynamoDB.
        documentDynamoDbTable.putItem(personDocument);

        // Retrieve the item just saved.
        EnhancedDocument srPerson = documentDynamoDbTable.getItem(Key.builder().partitionValue(50).sortValue("Rodriguez").build());

        // Access the addresses attribute.
        Map<String, Address> srAddresses = srPerson.get("addresses",
                EnhancedType.mapOf(EnhancedType.of(String.class), EnhancedType.of(Address.class)));

        srAddresses.keySet().forEach(k -> logger.info(addresses.get(k).toString()));

        documentDynamoDbTable.deleteTable();

// The content logged to the console shows that the saved maps were converted to Address instances.
Address{street='123 Main Street', city='Any Town', state='NC', zipCode='00000'}
Address{street='100 Any Street', city='Any Town', state='NC', zipCode='00000'}
```

## `CustomAttributeConverterProvider` コード
<a name="ddb-en-client-doc-api-convert-provider"></a>

```
public class CustomAttributeConverterProvider implements AttributeConverterProvider {

    private final Map<EnhancedType<?>, AttributeConverter<?>> converterCache = ImmutableMap.of(
            // 1. Add AddressConverter to the internal cache.
            EnhancedType.of(Address.class), new AddressConverter());

    public static CustomAttributeConverterProvider create() {
        return new CustomAttributeConverterProvider();
    }

    // 2. The enhanced client queries the provider for attribute converters if it
    //    encounters a type that it does not know how to convert.
    @SuppressWarnings("unchecked")
    @Override
    public <T> AttributeConverter<T> converterFor(EnhancedType<T> enhancedType) {
        return (AttributeConverter<T>) converterCache.get(enhancedType);
    }

    // 3. Custom attribute converter
    private class AddressConverter implements AttributeConverter<Address> {
        // 4. Transform an Address object into a DynamoDB map.
        @Override
        public AttributeValue transformFrom(Address address) {

            Map<String, AttributeValue> attributeValueMap = Map.of(
                    "street", AttributeValue.fromS(address.getStreet()),
                    "city", AttributeValue.fromS(address.getCity()),
                    "state", AttributeValue.fromS(address.getState()),
                    "zipCode", AttributeValue.fromS(address.getZipCode()));

            return AttributeValue.fromM(attributeValueMap);
        }

        // 5. Transform the DynamoDB map attribute to an Address oject.
        @Override
        public Address transformTo(AttributeValue attributeValue) {
            Map<String, AttributeValue> m = attributeValue.m();
            Address address = new Address();
            address.setStreet(m.get("street").s());
            address.setCity(m.get("city").s());
            address.setState(m.get("state").s());
            address.setZipCode(m.get("zipCode").s());

            return address;
        }

        @Override
        public EnhancedType<Address> type() {
            return EnhancedType.of(Address.class);
        }

        @Override
        public AttributeValueType attributeValueType() {
            return AttributeValueType.M;
        }
    }
}
```

## `Address` クラス
<a name="ddb-en-client-doc-api-convert-address"></a>

```
public class Address {
                  private String street;
                  private String city;
                  private String state;
                  private String zipCode;

                  public Address() {
                  }

                  public String getStreet() {
                  return this.street;
                  }

                  public String getCity() {
                  return this.city;
                  }

                  public String getState() {
                  return this.state;
                  }

                  public String getZipCode() {
                  return this.zipCode;
                  }

                  public void setStreet(String street) {
                  this.street = street;
                  }

                  public void setCity(String city) {
                  this.city = city;
                  }

                  public void setState(String state) {
                  this.state = state;
                  }

                  public void setZipCode(String zipCode) {
                  this.zipCode = zipCode;
                  }
                  }
```

## アドレスを提供するヘルパーメソッド
<a name="ddb-en-client-doc-api-convert-helper"></a>

次のヘルパーメソッドは、値にジェネリック `Map<String, String>` インスタンスではなく、値にカスタム `Address` インスタンスを使用するマップを提供します。

```
    private static Map<String, Address> getAddressesForCustomMappingExample() {
        Address homeAddress = new Address();
        homeAddress.setStreet("100 Any Street");
        homeAddress.setCity("Any Town");
        homeAddress.setState("NC");
        homeAddress.setZipCode("00000");

        Address workAddress = new Address();
        workAddress.setStreet("123 Main Street");
        workAddress.setCity("Any Town");
        workAddress.setState("NC");
        workAddress.setZipCode("00000");

        return Map.of("home", homeAddress,
                "work", workAddress);
    }
```

# DynamoDB を使用せずに `EnhancedDocument` を使用する
<a name="ddb-en-client-doc-api-standalone"></a>

通常、`EnhancedDocument` のインスタンスはドキュメントタイプの DynamoDB アイテムの読み取りと書き込みに使用しますが、DynamoDB とは独立して使用することもできます。

JSON 文字列やカスタムオブジェクトを、次の例のように `AttributeValues` の低レベルのマップに変換できるようにするために `EnhancedDocuments` を使用できます。

```
    public static void conversionWithoutDynamoDbExample() {
        Address address = new Address();
        address.setCity("my city");
        address.setState("my state");
        address.setStreet("my street");
        address.setZipCode("00000");

        // Build an EnhancedDocument instance for its conversion functionality alone.
        EnhancedDocument addressEnhancedDoc = EnhancedDocument.builder()
                // Important: You must specify attribute converter providers when you build an EnhancedDocument instance not used with a DynamoDB table.
                .attributeConverterProviders(new CustomAttributeConverterProvider(), DefaultAttributeConverterProvider.create())
                .put("addressDoc", address, Address.class)
                .build();

        // Convert address to a low-level item representation.
        final Map<String, AttributeValue> addressAsAttributeMap = addressEnhancedDoc.getMapOfUnknownType("addressDoc");
        logger.info("addressAsAttributeMap: {}", addressAsAttributeMap.toString());

        // Convert address to a JSON string.
        String addressAsJsonString = addressEnhancedDoc.getJson("addressDoc");
        logger.info("addressAsJsonString: {}", addressAsJsonString);
        // Convert addressEnhancedDoc back to an Address instance.
        Address addressConverted =  addressEnhancedDoc.get("addressDoc", Address.class);
        logger.info("addressConverted: {}", addressConverted.toString());
    }

   /* Console output:
          addressAsAttributeMap: {zipCode=AttributeValue(S=00000), state=AttributeValue(S=my state), street=AttributeValue(S=my street), city=AttributeValue(S=my city)}
          addressAsJsonString: {"zipCode":"00000","state":"my state","street":"my street","city":"my city"}
          addressConverted: Address{street='my street', city='my city', state='my state', zipCode='00000'}
   */
```

**注記**  
DynamoDB テーブルから独立した拡張ドキュメントを使用する場合は、必ずビルダーに属性コンバータープロバイダーを明示的に設定してください。  
対照的に、拡張ドキュメントを DynamoDB テーブルで使用すると、ドキュメントテーブルスキーマがコンバータープロバイダーに提供されます。