29
29
import java .util .Optional ;
30
30
import java .util .Set ;
31
31
import java .util .stream .Collectors ;
32
+ import java .util .stream .StreamSupport ;
32
33
import software .amazon .awssdk .annotations .Immutable ;
33
34
import software .amazon .awssdk .annotations .SdkInternalApi ;
34
35
import software .amazon .awssdk .core .SdkBytes ;
46
47
import software .amazon .awssdk .protocols .jsoncore .JsonNodeParser ;
47
48
import software .amazon .awssdk .services .dynamodb .model .AttributeValue ;
48
49
import software .amazon .awssdk .utils .Lazy ;
49
- import software .amazon .awssdk .utils .StringUtils ;
50
50
import software .amazon .awssdk .utils .Validate ;
51
51
52
52
/**
@@ -181,7 +181,6 @@ public String getJson(String attributeName) {
181
181
return null ;
182
182
}
183
183
return JSON_ATTRIBUTE_CONVERTER .transformTo (attributeValue ).toString ();
184
- // TODO: Does toString return valid JSON? will remove this after comparing V1 side by side.
185
184
}
186
185
187
186
@ Override
@@ -215,22 +214,16 @@ public Map<String, AttributeValue> getUnknownTypeMap(String attributeName) {
215
214
216
215
@ Override
217
216
public String toJson () {
218
- StringBuilder output = new StringBuilder ();
219
- output .append ('{' );
220
- boolean isFirst = true ;
221
- for (Map .Entry <String , AttributeValue > entry : attributeValueMap .getValue ().entrySet ()) {
222
- if (!isFirst ) {
223
- output .append (", " );
224
- } else {
225
- isFirst = false ;
226
- }
227
- output .append ('"' )
228
- .append (StringUtils .replace (entry .getKey (), "\" " , "\\ " ))
229
- .append ("\" : " )
230
- .append (JSON_ATTRIBUTE_CONVERTER .transformTo (entry .getValue ()));
217
+ if (nonAttributeValueMap .isEmpty ()) {
218
+ return "{}" ;
231
219
}
232
- output .append ('}' );
233
- return output .toString ();
220
+
221
+ return attributeValueMap .getValue ().entrySet ().stream ()
222
+ .map (entry -> "\" "
223
+ + addEscapeCharacters (entry .getKey ())
224
+ + "\" :"
225
+ + stringValue (JSON_ATTRIBUTE_CONVERTER .transformTo (entry .getValue ())))
226
+ .collect (Collectors .joining ("," , "{" , "}" ));
234
227
}
235
228
236
229
@ Override
@@ -286,6 +279,7 @@ public DefaultBuilder(DefaultEnhancedDocument enhancedDocument) {
286
279
287
280
public Builder putObject (String attributeName , Object value ) {
288
281
Validate .paramNotNull (attributeName , "attributeName" );
282
+ Validate .paramNotBlank (attributeName .trim (), "attributeName" );
289
283
enhancedTypeMap .remove (attributeName );
290
284
nonAttributeValueMap .remove (attributeName );
291
285
nonAttributeValueMap .put (attributeName , value );
@@ -435,6 +429,7 @@ private static AttributeValue getAttributeValueFromJson(String json) {
435
429
436
430
private static void checkInvalidAttribute (String attributeName , Object value ) {
437
431
Validate .paramNotNull (attributeName , "attributeName" );
432
+ Validate .paramNotBlank (attributeName .trim (), "attributeName" );
438
433
Validate .notNull (value , "%s must not be null. Use putNull API to insert a Null value" , value );
439
434
}
440
435
}
@@ -461,4 +456,68 @@ public int hashCode() {
461
456
return result ;
462
457
}
463
458
459
+ private static String stringValue (JsonNode jsonNode ) {
460
+ if (jsonNode .isArray ()) {
461
+ return StreamSupport .stream (jsonNode .asArray ().spliterator (), false )
462
+ .map (DefaultEnhancedDocument ::stringValue )
463
+ .collect (Collectors .joining ("," , "[" , "]" ));
464
+ }
465
+ if (jsonNode .isObject ()) {
466
+ return mapToString (jsonNode );
467
+ }
468
+
469
+ return jsonNode .isString () ? "\" " + addEscapeCharacters (jsonNode .text ()) + "\" " : jsonNode .toString ();
470
+ }
471
+
472
+ private static String addEscapeCharacters (String input ) {
473
+ StringBuilder output = new StringBuilder ();
474
+
475
+ for (int i = 0 ; i < input .length (); i ++) {
476
+ char ch = input .charAt (i );
477
+
478
+ switch (ch ) {
479
+ case '\\' :
480
+ output .append ("\\ \\ " ); // escape backslash with a backslash
481
+ break ;
482
+ case '\n' :
483
+ output .append ("\\ n" ); // newline character
484
+ break ;
485
+ case '\r' :
486
+ output .append ("\\ r" ); // carriage return character
487
+ break ;
488
+ case '\t' :
489
+ output .append ("\\ t" ); // tab character
490
+ break ;
491
+ case '\f' :
492
+ output .append ("\\ f" ); // form feed
493
+ break ;
494
+ case '\"' :
495
+ output .append ("\\ \" " ); // double-quote character
496
+ break ;
497
+ case '\'' :
498
+ output .append ("\\ '" ); // single-quote character
499
+ break ;
500
+ default :
501
+ output .append (ch );
502
+ break ;
503
+ }
504
+ }
505
+
506
+ return output .toString ();
507
+ }
508
+
509
+ private static String mapToString (JsonNode jsonNode ) {
510
+ Map <String , JsonNode > value = jsonNode .asObject ();
511
+
512
+ if (value .isEmpty ()) {
513
+ return "{}" ;
514
+ }
515
+
516
+ StringBuilder output = new StringBuilder ();
517
+ output .append ("{" );
518
+ value .forEach ((k , v ) -> output .append ("\" " ).append (k ).append ("\" :" )
519
+ .append (stringValue (v )).append ("," ));
520
+ output .setCharAt (output .length () - 1 , '}' );
521
+ return output .toString ();
522
+ }
464
523
}
0 commit comments