Skip to content

Commit 3ecf99a

Browse files
committed
Direct conversion from attrib map 4% performant
1 parent fd6b4a2 commit 3ecf99a

File tree

1 file changed

+178
-7
lines changed

1 file changed

+178
-7
lines changed

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

Lines changed: 178 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import software.amazon.awssdk.protocols.jsoncore.JsonNode;
4848
import software.amazon.awssdk.protocols.jsoncore.JsonNodeParser;
4949
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
50+
import software.amazon.awssdk.utils.BinaryUtils;
5051
import software.amazon.awssdk.utils.Lazy;
5152
import software.amazon.awssdk.utils.StringUtils;
5253
import software.amazon.awssdk.utils.Validate;
@@ -224,18 +225,188 @@ public Map<String, AttributeValue> getMapOfUnknownType(String attributeName) {
224225
}
225226
return attributeValue.m();
226227
}
227-
228228
@Override
229229
public String toJson() {
230230
if (nonAttributeValueMap.isEmpty()) {
231231
return "{}";
232232
}
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(",", "{", "}"));
233+
234+
StringBuilder sb = new StringBuilder(nonAttributeValueMap.size() * 50);
235+
sb.append('{');
236+
237+
boolean first = true;
238+
for (Map.Entry<String, Object> entry : nonAttributeValueMap.entrySet()) {
239+
if (!first) {
240+
sb.append(',');
241+
}
242+
first = false;
243+
244+
sb.append('"');
245+
appendEscaped(entry.getKey(), sb);
246+
sb.append("\":");
247+
248+
serializeValueDirect(sb, entry.getValue());
249+
}
250+
251+
sb.append('}');
252+
return sb.toString();
253+
}
254+
255+
private void serializeValueDirect(StringBuilder sb, Object value) {
256+
if (value == null || NULL_ATTRIBUTE_VALUE.equals(value)) {
257+
sb.append("null");
258+
} else if (value instanceof AttributeValue) {
259+
serializeAttributeValue(sb, (AttributeValue) value);
260+
} else if (value instanceof String) {
261+
sb.append('"');
262+
appendEscaped((String) value, sb);
263+
sb.append('"');
264+
} else if (value instanceof Number) {
265+
sb.append(value);
266+
} else if (value instanceof Boolean) {
267+
sb.append(value);
268+
} else if (value instanceof SdkBytes) {
269+
sb.append('"');
270+
sb.append(BinaryUtils.toBase64(((SdkBytes) value).asByteArray()));
271+
sb.append('"');
272+
} else if (value instanceof List) {
273+
serializeListDirect(sb, (List<?>) value);
274+
} else if (value instanceof Map) {
275+
serializeMapDirect(sb, (Map<?, ?>) value);
276+
} else if (value instanceof Set) {
277+
serializeSetDirect(sb, (Set<?>) value);
278+
} else {
279+
@SuppressWarnings("unchecked")
280+
AttributeValue av = toAttributeValue(value, (EnhancedType) EnhancedType.of(value.getClass()));
281+
serializeAttributeValue(sb, av);
282+
}
283+
}
284+
285+
private void serializeAttributeValue(StringBuilder sb, AttributeValue av) {
286+
if (av.nul() != null && av.nul()) {
287+
sb.append("null");
288+
} else if (av.s() != null) {
289+
sb.append('"');
290+
appendEscaped(av.s(), sb);
291+
sb.append('"');
292+
} else if (av.n() != null) {
293+
sb.append(av.n());
294+
} else if (av.bool() != null) {
295+
sb.append(av.bool());
296+
} else if (av.b() != null) {
297+
sb.append('"');
298+
sb.append(BinaryUtils.toBase64((av.b().asByteArray())));
299+
sb.append('"');
300+
} else if (av.hasL()) {
301+
sb.append('[');
302+
boolean first = true;
303+
for (AttributeValue item : av.l()) {
304+
if (!first) sb.append(',');
305+
first = false;
306+
serializeAttributeValue(sb, item);
307+
}
308+
sb.append(']');
309+
} else if (av.hasM()) {
310+
sb.append('{');
311+
boolean first = true;
312+
for (Map.Entry<String, AttributeValue> entry : av.m().entrySet()) {
313+
if (!first) sb.append(',');
314+
first = false;
315+
sb.append('"');
316+
appendEscaped(entry.getKey(), sb);
317+
sb.append("\":");
318+
serializeAttributeValue(sb, entry.getValue());
319+
}
320+
sb.append('}');
321+
} else if (av.hasSs()) {
322+
sb.append('[');
323+
boolean first = true;
324+
for (String s : av.ss()) {
325+
if (!first) sb.append(',');
326+
first = false;
327+
sb.append('"');
328+
appendEscaped(s, sb);
329+
sb.append('"');
330+
}
331+
sb.append(']');
332+
} else if (av.hasNs()) {
333+
sb.append('[');
334+
boolean first = true;
335+
for (String n : av.ns()) {
336+
if (!first) sb.append(',');
337+
first = false;
338+
sb.append(n);
339+
}
340+
sb.append(']');
341+
} else if (av.hasBs()) {
342+
sb.append('[');
343+
boolean first = true;
344+
for (SdkBytes b : av.bs()) {
345+
if (!first) sb.append(',');
346+
first = false;
347+
sb.append('"');
348+
sb.append(b.asUtf8String()); // Changed from asUtf8String()
349+
sb.append('"');
350+
}
351+
sb.append(']');
352+
}
353+
}
354+
355+
private void serializeListDirect(StringBuilder sb, List<?> list) {
356+
sb.append('[');
357+
boolean first = true;
358+
for (Object item : list) {
359+
if (!first) sb.append(',');
360+
first = false;
361+
serializeValueDirect(sb, item);
362+
}
363+
sb.append(']');
364+
}
365+
366+
private void serializeMapDirect(StringBuilder sb, Map<?, ?> map) {
367+
sb.append('{');
368+
boolean first = true;
369+
for (Map.Entry<?, ?> entry : map.entrySet()) {
370+
if (!first) sb.append(',');
371+
first = false;
372+
sb.append('"');
373+
appendEscaped(String.valueOf(entry.getKey()), sb);
374+
sb.append("\":");
375+
serializeValueDirect(sb, entry.getValue());
376+
}
377+
sb.append('}');
378+
}
379+
380+
private void serializeSetDirect(StringBuilder sb, Set<?> set) {
381+
sb.append('[');
382+
boolean first = true;
383+
for (Object item : set) {
384+
if (!first) sb.append(',');
385+
first = false;
386+
serializeValueDirect(sb, item);
387+
}
388+
sb.append(']');
389+
}
390+
391+
private void appendEscaped(String input, StringBuilder output) {
392+
for (int i = 0, len = input.length(); i < len; i++) {
393+
char ch = input.charAt(i);
394+
switch (ch) {
395+
case '\\': output.append("\\\\"); break;
396+
case '"': output.append("\\\""); break;
397+
case '\n': output.append("\\n"); break;
398+
case '\r': output.append("\\r"); break;
399+
case '\t': output.append("\\t"); break;
400+
case '\f': output.append("\\f"); break;
401+
case '\b': output.append("\\b"); break;
402+
default:
403+
if (ch < 0x20) {
404+
output.append(String.format("\\u%04X", (int) ch));
405+
} else {
406+
output.append(ch);
407+
}
408+
}
409+
}
239410
}
240411

241412
@Override

0 commit comments

Comments
 (0)