Skip to content

Remove extra spaces in Json and make it same as Items as in V1 #3835

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap;
import static software.amazon.awssdk.enhanced.dynamodb.internal.document.JsonStringFormatHelper.addEscapeCharacters;
import static software.amazon.awssdk.enhanced.dynamodb.internal.document.JsonStringFormatHelper.stringValue;

import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -46,7 +48,6 @@
import software.amazon.awssdk.protocols.jsoncore.JsonNodeParser;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.utils.Lazy;
import software.amazon.awssdk.utils.StringUtils;
import software.amazon.awssdk.utils.Validate;

/**
Expand Down Expand Up @@ -181,7 +182,6 @@ public String getJson(String attributeName) {
return null;
}
return JSON_ATTRIBUTE_CONVERTER.transformTo(attributeValue).toString();
// TODO: Does toString return valid JSON? will remove this after comparing V1 side by side.
}

@Override
Expand Down Expand Up @@ -215,22 +215,15 @@ public Map<String, AttributeValue> getUnknownTypeMap(String attributeName) {

@Override
public String toJson() {
StringBuilder output = new StringBuilder();
output.append('{');
boolean isFirst = true;
for (Map.Entry<String, AttributeValue> entry : attributeValueMap.getValue().entrySet()) {
if (!isFirst) {
output.append(", ");
} else {
isFirst = false;
}
output.append('"')
.append(StringUtils.replace(entry.getKey(), "\"", "\\"))
.append("\": ")
.append(JSON_ATTRIBUTE_CONVERTER.transformTo(entry.getValue()));
}
output.append('}');
return output.toString();
if (nonAttributeValueMap.isEmpty()) {
return "{}";
}
return attributeValueMap.getValue().entrySet().stream()
.map(entry -> "\""
+ addEscapeCharacters(entry.getKey())
+ "\":"
+ stringValue(JSON_ATTRIBUTE_CONVERTER.transformTo(entry.getValue())))
.collect(Collectors.joining(",", "{", "}"));
}

@Override
Expand Down Expand Up @@ -286,6 +279,7 @@ public DefaultBuilder(DefaultEnhancedDocument enhancedDocument) {

public Builder putObject(String attributeName, Object value) {
Validate.paramNotNull(attributeName, "attributeName");
Validate.paramNotBlank(attributeName.trim(), "attributeName");
enhancedTypeMap.remove(attributeName);
nonAttributeValueMap.remove(attributeName);
nonAttributeValueMap.put(attributeName, value);
Expand Down Expand Up @@ -435,6 +429,7 @@ private static AttributeValue getAttributeValueFromJson(String json) {

private static void checkInvalidAttribute(String attributeName, Object value) {
Validate.paramNotNull(attributeName, "attributeName");
Validate.paramNotBlank(attributeName.trim(), "attributeName");
Validate.notNull(value, "%s must not be null. Use putNull API to insert a Null value", value);
}
}
Expand All @@ -461,4 +456,5 @@ public int hashCode() {
return result;
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.awssdk.enhanced.dynamodb.internal.document;

import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.protocols.jsoncore.JsonNode;

@SdkInternalApi
public final class JsonStringFormatHelper {

private JsonStringFormatHelper() {
}

/**
* Helper function to convert a JsonNode to Json String representation
*
* @param jsonNode The JsonNode that needs to be converted to Json String.
* @return Json String of Json Node.
*/
public static String stringValue(JsonNode jsonNode) {
if (jsonNode.isArray()) {
return StreamSupport.stream(jsonNode.asArray().spliterator(), false)
.map(JsonStringFormatHelper::stringValue)
.collect(Collectors.joining(",", "[", "]"));
}
if (jsonNode.isObject()) {
return mapToString(jsonNode);
}
return jsonNode.isString() ? "\"" + addEscapeCharacters(jsonNode.text()) + "\"" : jsonNode.toString();
}

/**
* Escapes characters for a give given string
*
* @param input Input string
* @return String with escaped characters.
*/
public static String addEscapeCharacters(String input) {
StringBuilder output = new StringBuilder();
for (int i = 0; i < input.length(); i++) {
char ch = input.charAt(i);
switch (ch) {
case '\\':
output.append("\\\\"); // escape backslash with a backslash
break;
case '\n':
output.append("\\n"); // newline character
break;
case '\r':
output.append("\\r"); // carriage return character
break;
case '\t':
output.append("\\t"); // tab character
break;
case '\f':
output.append("\\f"); // form feed
break;
case '\"':
output.append("\\\""); // double-quote character
break;
case '\'':
output.append("\\'"); // single-quote character
break;
default:
output.append(ch);
break;
}
}
return output.toString();
}

private static String mapToString(JsonNode jsonNode) {
Map<String, JsonNode> value = jsonNode.asObject();

if (value.isEmpty()) {
return "{}";
}

StringBuilder output = new StringBuilder();
output.append("{");
value.forEach((k, v) -> output.append("\"").append(k).append("\":")
.append(stringValue(v)).append(","));
output.setCharAt(output.length() - 1, '}');
return output.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package software.amazon.awssdk.enhanced.dynamodb.document;

import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
import static software.amazon.awssdk.enhanced.dynamodb.AttributeConverterProvider.defaultProvider;
Expand All @@ -34,6 +35,10 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.core.SdkNumber;
import software.amazon.awssdk.enhanced.dynamodb.EnhancedType;
Expand Down Expand Up @@ -75,6 +80,7 @@ void enhancedDocumentGetters() {
assertThat(document.getMap("simpleMap", EnhancedType.of(UUID.class), EnhancedType.of(BigDecimal.class)))
.containsExactlyEntriesOf(expectedUuidBigDecimalMap);
}

@Test
void testNullArgsInStaticConstructor() {
assertThatNullPointerException()
Expand Down Expand Up @@ -114,8 +120,7 @@ void builder_ResetsTheOldValues_beforeJsonSetterIsCalled() {
.putString("simpleKeyNew", "simpleValueNew")
.build();

assertThat(enhancedDocument.toJson()).isEqualTo("{\"stringKey\": \"stringValue\", \"simpleKeyNew\": "
+ "\"simpleValueNew\"}");
assertThat(enhancedDocument.toJson()).isEqualTo("{\"stringKey\":\"stringValue\",\"simpleKeyNew\":\"simpleValueNew\"}");
assertThat(enhancedDocument.getString("simpleKeyOriginal")).isNull();

}
Expand Down Expand Up @@ -227,8 +232,8 @@ void error_When_DefaultProviderIsPlacedCustomProvider() {
EnhancedType.of(CustomClassForDocumentAPI.class))
.build();

assertThat(afterCustomClass.toJson()).isEqualTo("{\"direct_attr\": \"sample_value\", \"customObject\": "
+ "{\"longNumber\": 26,\"string\": \"str_one\"}}");
assertThat(afterCustomClass.toJson()).isEqualTo("{\"direct_attr\":\"sample_value\",\"customObject\":{\"longNumber\":26,"
+ "\"string\":\"str_one\"}}");

EnhancedDocument enhancedDocument = EnhancedDocument.builder()
.putString("direct_attr", "sample_value")
Expand All @@ -242,4 +247,40 @@ void error_When_DefaultProviderIsPlacedCustomProvider() {
).withMessage("Converter not found for "
+ "EnhancedType(software.amazon.awssdk.enhanced.dynamodb.converters.document.CustomClassForDocumentAPI)");
}

private static Stream<Arguments> escapeDocumentStrings() {
char c = 0x0a;
return Stream.of(
Arguments.of(String.valueOf(c),"{\"key\":\"\\n\"}")
, Arguments.of("","{\"key\":\"\"}")
, Arguments.of(" ","{\"key\":\" \"}")
, Arguments.of("\t","{\"key\":\"\\t\"}")
, Arguments.of("\n","{\"key\":\"\\n\"}")
, Arguments.of("\r","{\"key\":\"\\r\"}")
, Arguments.of("\f", "{\"key\":\"\\f\"}")
);
}

@ParameterizedTest
@ValueSource(strings = {"", " " , "\t", " ", "\n", "\r", "\f"})
void invalidKeyNames(String escapingString){
assertThatIllegalArgumentException().isThrownBy(() ->
EnhancedDocument.builder()
.attributeConverterProviders(defaultProvider())
.putString(escapingString, "sample")
.build())
.withMessageContaining("attributeName must not be blank or empty.");

}

@ParameterizedTest
@MethodSource("escapeDocumentStrings")
void escapingTheValues(String escapingString, String expectedJson) {

EnhancedDocument document = EnhancedDocument.builder()
.attributeConverterProviders(defaultProvider())
.putString("key", escapingString)
.build();
assertThat(document.toJson()).isEqualTo(expectedJson);
}
}
Loading