diff --git a/.changes/next-release/bugfix-DynamoDBEnhancedClient-efd901b.json b/.changes/next-release/bugfix-DynamoDBEnhancedClient-efd901b.json new file mode 100644 index 000000000000..6885f58bdca2 --- /dev/null +++ b/.changes/next-release/bugfix-DynamoDBEnhancedClient-efd901b.json @@ -0,0 +1,6 @@ +{ + "category": "DynamoDB Enhanced Client", + "contributor": "", + "type": "bugfix", + "description": "Converts string to NULL attribute if it's null. See [#2243](https://github.com/aws/aws-sdk-java-v2/issues/2243)" +} diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/StringAttributeConverter.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/StringAttributeConverter.java index 542d8e84ec84..bf558d03dbbd 100644 --- a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/StringAttributeConverter.java +++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/StringAttributeConverter.java @@ -29,6 +29,7 @@ import software.amazon.awssdk.enhanced.dynamodb.AttributeConverter; import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType; import software.amazon.awssdk.enhanced.dynamodb.EnhancedType; +import software.amazon.awssdk.enhanced.dynamodb.internal.AttributeValues; import software.amazon.awssdk.enhanced.dynamodb.internal.converter.TypeConvertingVisitor; import software.amazon.awssdk.enhanced.dynamodb.internal.converter.string.BooleanStringConverter; import software.amazon.awssdk.enhanced.dynamodb.internal.converter.string.ByteArrayStringConverter; @@ -64,7 +65,7 @@ public AttributeValueType attributeValueType() { @Override public AttributeValue transformFrom(String input) { - return AttributeValue.builder().s(input).build(); + return input == null ? AttributeValues.nullAttributeValue() : AttributeValue.builder().s(input).build(); } @Override diff --git a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/converters/attribute/StringAttributeConvertersTest.java b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/converters/attribute/StringAttributeConvertersTest.java index 75c121bd0198..2a6f608c8ad5 100644 --- a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/converters/attribute/StringAttributeConvertersTest.java +++ b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/converters/attribute/StringAttributeConvertersTest.java @@ -40,6 +40,7 @@ import org.junit.Test; import software.amazon.awssdk.core.SdkBytes; import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType; +import software.amazon.awssdk.enhanced.dynamodb.internal.AttributeValues; import software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.CharSequenceAttributeConverter; import software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.CharacterArrayAttributeConverter; import software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.CharacterAttributeConverter; @@ -128,6 +129,7 @@ public void stringAttributeConverterBehaves() { String chars = "foo"; String numChars = "42"; + assertThat(transformFrom(converter, null)).isSameAs(AttributeValues.nullAttributeValue()); assertThat(transformFrom(converter, chars).s()).isSameAs(chars); assertThat(transformFrom(converter, emptyChars).s()).isSameAs(emptyChars); diff --git a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/mapper/BeanTableSchemaTest.java b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/mapper/BeanTableSchemaTest.java index 41c8e0bf0685..0ffc80c973bb 100644 --- a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/mapper/BeanTableSchemaTest.java +++ b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/mapper/BeanTableSchemaTest.java @@ -845,6 +845,35 @@ public void mapBean_stringStringMap() { assertThat(reverse, is(equalTo(mapBean))); } + @Test + public void mapBean_mapWithNullValue() { + BeanTableSchema beanTableSchema = BeanTableSchema.create(MapBean.class); + MapBean mapBean = new MapBean(); + mapBean.setId("id-value"); + + Map testMap = new HashMap<>(); + testMap.put("one", null); + testMap.put("three", "four"); + + mapBean.setStringMap(testMap); + + Map itemMap = beanTableSchema.itemToMap(mapBean, true); + + Map expectedMap = new HashMap<>(); + expectedMap.put("one", AttributeValues.nullAttributeValue()); + expectedMap.put("three", stringValue("four")); + AttributeValue expectedMapValue = AttributeValue.builder() + .m(expectedMap) + .build(); + + assertThat(itemMap.size(), is(2)); + assertThat(itemMap, hasEntry("id", stringValue("id-value"))); + assertThat(itemMap, hasEntry("stringMap", expectedMapValue)); + + MapBean reverse = beanTableSchema.mapToItem(itemMap); + assertThat(reverse, is(equalTo(mapBean))); + } + @Test public void mapBean_nestedStringMap() { BeanTableSchema beanTableSchema = BeanTableSchema.create(MapBean.class); diff --git a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticTableSchemaTest.java b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticTableSchemaTest.java index 7ef020a15d52..fc43da907b08 100644 --- a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticTableSchemaTest.java +++ b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticTableSchemaTest.java @@ -1216,6 +1216,24 @@ public void mapperCanHandleGenericMap() { AttributeValue.builder().m(attributeValueMap).build()); } + @Test + public void mapperCanHandleMapWithNullValue() { + Map stringMap = new HashMap<>(); + stringMap.put("one", null); + stringMap.put("two", "three"); + + Map attributeValueMap = new HashMap<>(); + attributeValueMap.put("one", AttributeValue.builder().nul(true).build()); + attributeValueMap.put("two", AttributeValue.builder().s("three").build()); + + verifyNullableAttribute(EnhancedType.mapOf(String.class, String.class), + a -> a.name("value") + .getter(FakeMappedItem::getAStringMap) + .setter(FakeMappedItem::setAStringMap), + FakeMappedItem.builder().aStringMap(stringMap).build(), + AttributeValue.builder().m(attributeValueMap).build()); + } + @Test public void mapperCanHandleIntDoubleMap() { Map intDoubleMap = new ConcurrentHashMap<>();