-
Notifications
You must be signed in to change notification settings - Fork 976
Open
Labels
bugThis issue is a bug.This issue is a bug.needs-triageThis issue or PR still needs to be triaged.This issue or PR still needs to be triaged.
Description
Describe the bug
TableSchema.mapToItem supports converting a map attribute value to a string representation but throws NullPointerException when converting a map AttributeValue containing NUL.
Regression Issue
- Select this option if this issue appears to be a regression.
Expected Behavior
The deserialized string should contain the string representation of the map including null values, and not throw.
Current Behavior
Throws exception:
java.lang.NullPointerException
at java.base/java.util.HashMap.merge(HashMap.java:1363)
at java.base/java.util.stream.Collectors.lambda$toMap$68(Collectors.java:1636)
at java.base/java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.base/java.util.Collections$UnmodifiableMap$UnmodifiableEntrySet.lambda$entryConsumer$0(Collections.java:1779)
at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1939)
at java.base/java.util.Collections$UnmodifiableMap$UnmodifiableEntrySet$UnmodifiableEntrySetSpliterator.forEachRemaining(Collections.java:1804)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
at software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.StringAttributeConverter$Visitor.convertMap(StringAttributeConverter.java:128)
at software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.StringAttributeConverter$Visitor.convertMap(StringAttributeConverter.java:76)
at software.amazon.awssdk.enhanced.dynamodb.internal.converter.TypeConvertingVisitor.convert(TypeConvertingVisitor.java:88)
at software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.EnhancedAttributeValue.convert(EnhancedAttributeValue.java:428)
at software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.StringAttributeConverter$Visitor.toString(StringAttributeConverter.java:142)
at software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.StringAttributeConverter.transformTo(StringAttributeConverter.java:73)
at software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.StringAttributeConverter.transformTo(StringAttributeConverter.java:48)
at software.amazon.awssdk.enhanced.dynamodb.internal.mapper.StaticAttributeType.attributeValueToObject(StaticAttributeType.java:46)
at software.amazon.awssdk.enhanced.dynamodb.internal.mapper.ResolvedImmutableAttribute.lambda$create$1(ResolvedImmutableAttribute.java:67)
at software.amazon.awssdk.enhanced.dynamodb.mapper.StaticImmutableTableSchema.mapToItem(StaticImmutableTableSchema.java:548)
at software.amazon.awssdk.enhanced.dynamodb.mapper.WrappedTableSchema.mapToItem(WrappedTableSchema.java:62)
at com.ddbtest.NullAttributeTest.testNullAttributeDeserialization(NullAttributeTest.java:72)
Reproduction Steps
Test to recreate:
package com.ddbtest;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.HashMap;
import java.util.Map;
import lombok.Builder;
import lombok.Value;
import org.junit.jupiter.api.Test;
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbImmutable;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
class NullAttributeTest {
@Value
@Builder(toBuilder = true)
@DynamoDbImmutable(builder = ClassWithMap.ClassWithMapBuilder.class)
public static class ClassWithMap {
Map<String, String> field;
}
@Value
@Builder(toBuilder = true)
@DynamoDbImmutable(builder = ClassWithString.ClassWithStringBuilder.class)
public static class ClassWithString {
String field;
}
@Test
void testNullAttributeDeserialization() {
Map<String, AttributeValue> attributesWithANull =
Map.of(
"field",
AttributeValue.builder()
.m(
Map.of(
"stringField", AttributeValue.builder().s("value").build(),
"nullField", AttributeValue.builder().nul(true).build()))
.build());
Map<String, AttributeValue> attributes =
Map.of(
"field",
AttributeValue.builder()
.m(Map.of("stringField", AttributeValue.builder().s("value").build()))
.build());
{
ClassWithMap result =
TableSchema.fromClass(ClassWithMap.class).mapToItem(attributesWithANull, false);
ClassWithMap expected =
ClassWithMap.builder()
.field(
new HashMap<>() {
{
put("stringField", "value");
put("nullField", null);
}
})
.build();
assertEquals(expected, result);
}
{
ClassWithString result =
TableSchema.fromClass(ClassWithString.class).mapToItem(attributes, false);
ClassWithString expected = ClassWithString.builder().field("{stringField=value}").build();
assertEquals(expected, result);
}
assertThrows(
NullPointerException.class,
() -> TableSchema.fromClass(ClassWithString.class).mapToItem(attributesWithANull, false));
}
}
Possible Solution
No response
Additional Information/Context
No response
AWS Java SDK version used
2.0.841071.0
JDK version used
openjdk 21.0.8 2025-07-15 LTS
Operating System and version
macOS 15.7.1
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
bugThis issue is a bug.This issue is a bug.needs-triageThis issue or PR still needs to be triaged.This issue or PR still needs to be triaged.