Skip to content

Commit 678af8f

Browse files
authored
Serialization optimization for DDB enhanced Client (#6507)
* adding strategy serialization optimization * uncomment new codepath * refactor to use jackson directly * use SdkJsonGenerator * Add test coverage * remove old toJson codepath * refactor serialize to use jackson directly * Refactor implementation * Refactor Serializer to share factory * Fix checkstyle * Refactoring based on comments from maintainer * uncomment code * Fix maintainer comments * Add changelog * Add fixture tests to cover binary handling edge cases * Update dependencies to use new jackson * trigger PR diff refresh * Add missing dependencies * Fix checkstyle
1 parent 8481844 commit 678af8f

File tree

16 files changed

+565
-9
lines changed

16 files changed

+565
-9
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "feature",
3+
"category": "Amazon DynamoDB Enhanced Client",
4+
"contributor": "",
5+
"description": "Optimize implementation of the `EnhancedDocument#toJson()` and `EnhancedDocument#getJson()`."
6+
}

services-custom/dynamodb-enhanced/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,16 @@
141141
<artifactId>utils</artifactId>
142142
<version>${awsjavasdk.version}</version>
143143
</dependency>
144+
<dependency>
145+
<groupId>software.amazon.awssdk</groupId>
146+
<artifactId>third-party-jackson-core</artifactId>
147+
<version>${awsjavasdk.version}</version>
148+
</dependency>
149+
<dependency>
150+
<groupId>software.amazon.awssdk</groupId>
151+
<artifactId>aws-json-protocol</artifactId>
152+
<version>${awsjavasdk.version}</version>
153+
</dependency>
144154
<dependency>
145155
<groupId>com.google.guava</groupId>
146156
<artifactId>guava</artifactId>

services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/document/DefaultEnhancedDocument.java

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717

1818
import static java.util.Collections.unmodifiableList;
1919
import static java.util.Collections.unmodifiableMap;
20-
import static software.amazon.awssdk.enhanced.dynamodb.internal.document.JsonStringFormatHelper.addEscapeCharacters;
21-
import static software.amazon.awssdk.enhanced.dynamodb.internal.document.JsonStringFormatHelper.stringValue;
2220

2321
import java.util.ArrayList;
2422
import java.util.Arrays;
@@ -193,7 +191,7 @@ public String getJson(String attributeName) {
193191
if (attributeValue == null) {
194192
return null;
195193
}
196-
return stringValue(JSON_ATTRIBUTE_CONVERTER.transformTo(attributeValue));
194+
return DocumentJsonSerializer.serializeSingleAttributeValue(attributeValue);
197195
}
198196

199197
@Override
@@ -230,12 +228,7 @@ public String toJson() {
230228
if (nonAttributeValueMap.isEmpty()) {
231229
return "{}";
232230
}
233-
return attributeValueMap.getValue().entrySet().stream()
234-
.map(entry -> "\""
235-
+ addEscapeCharacters(entry.getKey())
236-
+ "\":"
237-
+ stringValue(JSON_ATTRIBUTE_CONVERTER.transformTo(entry.getValue())))
238-
.collect(Collectors.joining(",", "{", "}"));
231+
return DocumentJsonSerializer.serializeAttributeValueMap(attributeValueMap.getValue());
239232
}
240233

241234
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.enhanced.dynamodb.internal.document;
17+
18+
import java.io.IOException;
19+
import java.io.OutputStream;
20+
import java.nio.charset.StandardCharsets;
21+
import java.util.Map;
22+
import software.amazon.awssdk.annotations.SdkInternalApi;
23+
import software.amazon.awssdk.core.SdkBytes;
24+
import software.amazon.awssdk.protocols.json.SdkJsonGenerator;
25+
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
26+
import software.amazon.awssdk.thirdparty.jackson.core.JsonFactory;
27+
import software.amazon.awssdk.thirdparty.jackson.core.JsonGenerator;
28+
29+
/**
30+
* JSON serializer for DynamoDB Enhanced Client document operations.
31+
*/
32+
@SdkInternalApi
33+
final class DocumentJsonSerializer {
34+
private static final JsonFactory JSON_FACTORY = new JsonFactory() {
35+
@Override
36+
public JsonGenerator createGenerator(OutputStream out) throws IOException {
37+
return super.createGenerator(out).enable(JsonGenerator.Feature.COMBINE_UNICODE_SURROGATES_IN_UTF8);
38+
}
39+
};
40+
41+
private DocumentJsonSerializer() {
42+
}
43+
44+
public static String serializeAttributeValueMap(Map<String, AttributeValue> map) {
45+
SdkJsonGenerator jsonGen = new SdkJsonGenerator(JSON_FACTORY, "application/json");
46+
47+
jsonGen.writeStartObject();
48+
map.forEach((key, value) -> {
49+
jsonGen.writeFieldName(key);
50+
serializeAttributeValue(jsonGen, value);
51+
});
52+
jsonGen.writeEndObject();
53+
54+
return new String(jsonGen.getBytes(), StandardCharsets.UTF_8);
55+
}
56+
57+
public static String serializeSingleAttributeValue(AttributeValue av) {
58+
SdkJsonGenerator jsonGen = new SdkJsonGenerator(JSON_FACTORY, "application/json");
59+
serializeAttributeValue(jsonGen, av);
60+
return new String(jsonGen.getBytes(), StandardCharsets.UTF_8);
61+
}
62+
63+
public static void serializeAttributeValue(SdkJsonGenerator generator, AttributeValue av) {
64+
switch (av.type()) {
65+
case NUL:
66+
generator.writeNull();
67+
break;
68+
case S:
69+
generator.writeValue(av.s());
70+
break;
71+
case N:
72+
generator.writeNumber(av.n());
73+
break;
74+
case BOOL:
75+
generator.writeValue(av.bool());
76+
break;
77+
case B:
78+
generator.writeValue(av.b().asByteBuffer());
79+
break;
80+
case L:
81+
generator.writeStartArray();
82+
for (AttributeValue item : av.l()) {
83+
serializeAttributeValue(generator, item);
84+
}
85+
generator.writeEndArray();
86+
break;
87+
case M:
88+
generator.writeStartObject();
89+
for (Map.Entry<String, AttributeValue> entry : av.m().entrySet()) {
90+
generator.writeFieldName(entry.getKey());
91+
serializeAttributeValue(generator, entry.getValue());
92+
}
93+
generator.writeEndObject();
94+
break;
95+
case SS:
96+
generator.writeStartArray();
97+
for (String s : av.ss()) {
98+
generator.writeValue(s);
99+
}
100+
generator.writeEndArray();
101+
break;
102+
case NS:
103+
generator.writeStartArray();
104+
for (String n : av.ns()) {
105+
generator.writeNumber(n);
106+
}
107+
generator.writeEndArray();
108+
break;
109+
case BS:
110+
generator.writeStartArray();
111+
for (SdkBytes b : av.bs()) {
112+
generator.writeValue(b.asByteBuffer());
113+
}
114+
generator.writeEndArray();
115+
break;
116+
default:
117+
throw new IllegalArgumentException("Unsupported AttributeValue type: " + av.type());
118+
}
119+
}
120+
121+
}

services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/document/DocumentTableSchemaTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.junit.jupiter.params.ParameterizedTest;
3232
import org.junit.jupiter.params.provider.ArgumentsSource;
3333
import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType;
34+
import software.amazon.awssdk.enhanced.dynamodb.DefaultAttributeConverterProvider;
3435
import software.amazon.awssdk.enhanced.dynamodb.EnhancedType;
3536
import software.amazon.awssdk.enhanced.dynamodb.TableMetadata;
3637
import software.amazon.awssdk.enhanced.dynamodb.converters.document.CustomAttributeForDocumentConverterProvider;

0 commit comments

Comments
 (0)