From 19f54ee6ed33b7517c729c801bc57c8c0478be7d Mon Sep 17 00:00:00 2001 From: Mihai Nita Date: Sun, 12 Feb 2023 08:20:02 -0800 Subject: [PATCH] Fixes #776: Add settings for the kind of newline to use (#2231) * Add settings for kind of newline to use * Fix amp in javadoc * Fixing link in javadoc * Doc: use JSON instead of Json Co-authored-by: Marcono1234 * PR Feedback: Objects.requireNonNull Co-authored-by: Marcono1234 * PR Feedback: $next-version$, don't hardcode Co-authored-by: Marcono1234 * s/testNewlineLF/testNewlineLf/ Co-authored-by: Marcono1234 * Implement PR feedback * Round two of review * Restore copyright year, no reason to update * Rename OS named enum values to CR and LF * Add javadoc to NewlineStyle.getValue() * Implement PR feedback, round 2 * Fix typo Co-authored-by: Marcono1234 * No need for line break Co-authored-by: Marcono1234 * Shorter, cleaner doc Co-authored-by: Marcono1234 * Using a FormattingStyle for pretty print * Fix Junit4 and Truth after merge from master * Implement review feedback * Double backslash in message --------- Co-authored-by: Marcono1234 --- .../java/com/google/gson/FormattingStyle.java | 98 +++++++++++ gson/src/main/java/com/google/gson/Gson.java | 31 ++-- .../java/com/google/gson/GsonBuilder.java | 27 +++- .../java/com/google/gson/JsonPrimitive.java | 2 +- .../com/google/gson/stream/JsonWriter.java | 47 +++++- gson/src/main/java/module-info.java | 16 +- .../test/java/com/google/gson/GsonTest.java | 6 +- .../gson/functional/FormattingStyleTest.java | 152 ++++++++++++++++++ .../internal/bind/JsonTreeWriterTest.java | 8 +- .../google/gson/stream/JsonWriterTest.java | 27 ++++ 10 files changed, 371 insertions(+), 43 deletions(-) create mode 100644 gson/src/main/java/com/google/gson/FormattingStyle.java create mode 100644 gson/src/test/java/com/google/gson/functional/FormattingStyleTest.java diff --git a/gson/src/main/java/com/google/gson/FormattingStyle.java b/gson/src/main/java/com/google/gson/FormattingStyle.java new file mode 100644 index 00000000..19b307aa --- /dev/null +++ b/gson/src/main/java/com/google/gson/FormattingStyle.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2022 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 com.google.gson; + +import java.util.Objects; + +/** + * A class used to control what the serialization looks like. + * + *

It currently defines the kind of newline to use, and the indent, but + * might add more in the future.

+ * + * @see Wikipedia Newline article + * + * @since $next-version$ + */ +public class FormattingStyle { + private final String newline; + private final String indent; + + static public final FormattingStyle DEFAULT = + new FormattingStyle("\n", " "); + + private FormattingStyle(String newline, String indent) { + Objects.requireNonNull(newline, "newline == null"); + Objects.requireNonNull(indent, "indent == null"); + if (!newline.matches("[\r\n]*")) { + throw new IllegalArgumentException( + "Only combinations of \\n and \\r are allowed in newline."); + } + if (!indent.matches("[ \t]*")) { + throw new IllegalArgumentException( + "Only combinations of spaces and tabs allowed in indent."); + } + this.newline = newline; + this.indent = indent; + } + + /** + * Creates a {@link FormattingStyle} with the specified newline setting. + * + *

It can be used to accommodate certain OS convention, for example + * hardcode {@code "\r"} for Linux and macos, {@code "\r\n"} for Windows, or + * call {@link java.lang.System#lineSeparator()} to match the current OS.

+ * + *

Only combinations of {@code \n} and {@code \r} are allowed.

+ * + * @param newline the string value that will be used as newline. + * @return a newly created {@link FormattingStyle} + */ + public FormattingStyle withNewline(String newline) { + return new FormattingStyle(newline, this.indent); + } + + /** + * Creates a {@link FormattingStyle} with the specified indent string. + * + *

Only combinations of spaces and tabs allowed in indent.

+ * + * @param indent the string value that will be used as indent. + * @return a newly created {@link FormattingStyle} + */ + public FormattingStyle withIndent(String indent) { + return new FormattingStyle(this.newline, indent); + } + + /** + * The string value that will be used as a newline. + * + * @return the newline value. + */ + public String getNewline() { + return this.newline; + } + + /** + * The string value that will be used as indent. + * + * @return the indent value. + */ + public String getIndent() { + return this.indent; + } +} diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index 0339d569..34f92fab 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -69,8 +69,8 @@ import java.util.concurrent.atomic.AtomicLongArray; * *

You can create a Gson instance by invoking {@code new Gson()} if the default configuration * is all you need. You can also use {@link GsonBuilder} to build a Gson instance with various - * configuration options such as versioning support, pretty printing, custom - * {@link JsonSerializer}s, {@link JsonDeserializer}s, and {@link InstanceCreator}s.

+ * configuration options such as versioning support, pretty printing, custom newline, custom indent, + * custom {@link JsonSerializer}s, {@link JsonDeserializer}s, and {@link InstanceCreator}s.

* *

Here is an example of how Gson is used for a simple Class: * @@ -141,7 +141,7 @@ import java.util.concurrent.atomic.AtomicLongArray; public final class Gson { static final boolean DEFAULT_JSON_NON_EXECUTABLE = false; static final boolean DEFAULT_LENIENT = false; - static final boolean DEFAULT_PRETTY_PRINT = false; + static final FormattingStyle DEFAULT_FORMATTING_STYLE = null; static final boolean DEFAULT_ESCAPE_HTML = true; static final boolean DEFAULT_SERIALIZE_NULLS = false; static final boolean DEFAULT_COMPLEX_MAP_KEYS = false; @@ -182,7 +182,7 @@ public final class Gson { final boolean complexMapKeySerialization; final boolean generateNonExecutableJson; final boolean htmlSafe; - final boolean prettyPrinting; + final FormattingStyle formattingStyle; final boolean lenient; final boolean serializeSpecialFloatingPointValues; final boolean useJdkUnsafe; @@ -202,7 +202,9 @@ public final class Gson { *

    *
  • The JSON generated by toJson methods is in compact representation. This * means that all the unneeded white-space is removed. You can change this behavior with - * {@link GsonBuilder#setPrettyPrinting()}.
  • + * {@link GsonBuilder#setPrettyPrinting()}. + *
  • When the JSON generated contains more than one line, the kind of newline and indent to + * use can be configured with {@link GsonBuilder#setPrettyPrinting(FormattingStyle)}.
  • *
  • The generated JSON omits all the fields that are null. Note that nulls in arrays are * kept as is since an array is an ordered list. Moreover, if a field is not null, but its * generated JSON is empty, the field is kept. You can configure Gson to serialize null values @@ -234,7 +236,7 @@ public final class Gson { this(Excluder.DEFAULT, DEFAULT_FIELD_NAMING_STRATEGY, Collections.>emptyMap(), DEFAULT_SERIALIZE_NULLS, DEFAULT_COMPLEX_MAP_KEYS, DEFAULT_JSON_NON_EXECUTABLE, DEFAULT_ESCAPE_HTML, - DEFAULT_PRETTY_PRINT, DEFAULT_LENIENT, DEFAULT_SPECIALIZE_FLOAT_VALUES, + DEFAULT_FORMATTING_STYLE, DEFAULT_LENIENT, DEFAULT_SPECIALIZE_FLOAT_VALUES, DEFAULT_USE_JDK_UNSAFE, LongSerializationPolicy.DEFAULT, DEFAULT_DATE_PATTERN, DateFormat.DEFAULT, DateFormat.DEFAULT, Collections.emptyList(), Collections.emptyList(), @@ -245,7 +247,7 @@ public final class Gson { Gson(Excluder excluder, FieldNamingStrategy fieldNamingStrategy, Map> instanceCreators, boolean serializeNulls, boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe, - boolean prettyPrinting, boolean lenient, boolean serializeSpecialFloatingPointValues, + FormattingStyle formattingStyle, boolean lenient, boolean serializeSpecialFloatingPointValues, boolean useJdkUnsafe, LongSerializationPolicy longSerializationPolicy, String datePattern, int dateStyle, int timeStyle, List builderFactories, @@ -261,7 +263,7 @@ public final class Gson { this.complexMapKeySerialization = complexMapKeySerialization; this.generateNonExecutableJson = generateNonExecutableGson; this.htmlSafe = htmlSafe; - this.prettyPrinting = prettyPrinting; + this.formattingStyle = formattingStyle; this.lenient = lenient; this.serializeSpecialFloatingPointValues = serializeSpecialFloatingPointValues; this.useJdkUnsafe = useJdkUnsafe; @@ -702,7 +704,7 @@ public final class Gson { *
        * Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
        * 
    - * @return Json representation of {@code src} + * @return JSON representation of {@code src}. * @since 1.4 * * @see #toJsonTree(Object) @@ -724,7 +726,7 @@ public final class Gson { * {@link Writer}, use {@link #toJson(Object, Appendable)} instead. * * @param src the object for which JSON representation is to be created - * @return Json representation of {@code src}. + * @return JSON representation of {@code src}. * * @see #toJson(Object, Appendable) * @see #toJson(Object, Type) @@ -749,7 +751,7 @@ public final class Gson { *
        * Type typeOfSrc = new TypeToken<Collection<Foo>>(){}.getType();
        * 
    - * @return JSON representation of {@code src} + * @return JSON representation of {@code src}. * * @see #toJson(Object, Type, Appendable) * @see #toJson(Object) @@ -855,7 +857,7 @@ public final class Gson { * Converts a tree of {@link JsonElement}s into its equivalent JSON representation. * * @param jsonElement root of a tree of {@link JsonElement}s - * @return JSON String representation of the tree + * @return JSON String representation of the tree. * @since 1.4 */ public String toJson(JsonElement jsonElement) { @@ -891,6 +893,7 @@ public final class Gson { *
  • {@link GsonBuilder#serializeNulls()}
  • *
  • {@link GsonBuilder#setLenient()}
  • *
  • {@link GsonBuilder#setPrettyPrinting()}
  • + *
  • {@link GsonBuilder#setPrettyPrinting(FormattingStyle)}
  • *
*/ public JsonWriter newJsonWriter(Writer writer) throws IOException { @@ -898,9 +901,7 @@ public final class Gson { writer.write(JSON_NON_EXECUTABLE_PREFIX); } JsonWriter jsonWriter = new JsonWriter(writer); - if (prettyPrinting) { - jsonWriter.setIndent(" "); - } + jsonWriter.setFormattingStyle(formattingStyle); jsonWriter.setHtmlSafe(htmlSafe); jsonWriter.setLenient(lenient); jsonWriter.setSerializeNulls(serializeNulls); diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index 8b04430f..63c7f047 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -23,7 +23,7 @@ import static com.google.gson.Gson.DEFAULT_JSON_NON_EXECUTABLE; import static com.google.gson.Gson.DEFAULT_LENIENT; import static com.google.gson.Gson.DEFAULT_NUMBER_TO_NUMBER_STRATEGY; import static com.google.gson.Gson.DEFAULT_OBJECT_TO_NUMBER_STRATEGY; -import static com.google.gson.Gson.DEFAULT_PRETTY_PRINT; +import static com.google.gson.Gson.DEFAULT_FORMATTING_STYLE; import static com.google.gson.Gson.DEFAULT_SERIALIZE_NULLS; import static com.google.gson.Gson.DEFAULT_SPECIALIZE_FLOAT_VALUES; import static com.google.gson.Gson.DEFAULT_USE_JDK_UNSAFE; @@ -98,7 +98,7 @@ public final class GsonBuilder { private boolean complexMapKeySerialization = DEFAULT_COMPLEX_MAP_KEYS; private boolean serializeSpecialFloatingPointValues = DEFAULT_SPECIALIZE_FLOAT_VALUES; private boolean escapeHtmlChars = DEFAULT_ESCAPE_HTML; - private boolean prettyPrinting = DEFAULT_PRETTY_PRINT; + private FormattingStyle formattingStyle = DEFAULT_FORMATTING_STYLE; private boolean generateNonExecutableJson = DEFAULT_JSON_NON_EXECUTABLE; private boolean lenient = DEFAULT_LENIENT; private boolean useJdkUnsafe = DEFAULT_USE_JDK_UNSAFE; @@ -129,7 +129,7 @@ public final class GsonBuilder { this.complexMapKeySerialization = gson.complexMapKeySerialization; this.generateNonExecutableJson = gson.generateNonExecutableJson; this.escapeHtmlChars = gson.htmlSafe; - this.prettyPrinting = gson.prettyPrinting; + this.formattingStyle = gson.formattingStyle; this.lenient = gson.lenient; this.serializeSpecialFloatingPointValues = gson.serializeSpecialFloatingPointValues; this.longSerializationPolicy = gson.longSerializationPolicy; @@ -478,13 +478,26 @@ public final class GsonBuilder { } /** - * Configures Gson to output Json that fits in a page for pretty printing. This option only - * affects Json serialization. + * Configures Gson to output JSON that fits in a page for pretty printing. This option only + * affects JSON serialization. * * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern */ public GsonBuilder setPrettyPrinting() { - prettyPrinting = true; + return setPrettyPrinting(FormattingStyle.DEFAULT); + } + + /** + * Configures Gson to output JSON that uses a certain kind of formatting stile (for example newline and indent). + * This option only affects JSON serialization. + * + *

Has no effect if the serialized format is a single line.

+ * + * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern + * @since $next-version$ + */ + public GsonBuilder setPrettyPrinting(FormattingStyle formattingStyle) { + this.formattingStyle = formattingStyle; return this; } @@ -761,7 +774,7 @@ public final class GsonBuilder { return new Gson(excluder, fieldNamingPolicy, new HashMap<>(instanceCreators), serializeNulls, complexMapKeySerialization, - generateNonExecutableJson, escapeHtmlChars, prettyPrinting, lenient, + generateNonExecutableJson, escapeHtmlChars, formattingStyle, lenient, serializeSpecialFloatingPointValues, useJdkUnsafe, longSerializationPolicy, datePattern, dateStyle, timeStyle, new ArrayList<>(this.factories), new ArrayList<>(this.hierarchyFactories), factories, diff --git a/gson/src/main/java/com/google/gson/JsonPrimitive.java b/gson/src/main/java/com/google/gson/JsonPrimitive.java index fce9093f..827de959 100644 --- a/gson/src/main/java/com/google/gson/JsonPrimitive.java +++ b/gson/src/main/java/com/google/gson/JsonPrimitive.java @@ -106,7 +106,7 @@ public final class JsonPrimitive extends JsonElement { if (isBoolean()) { return (Boolean) value; } - // Check to see if the value as a String is "true" in any case. + // Check to see if the value as a String is "true" in any case. return Boolean.parseBoolean(getAsString()); } diff --git a/gson/src/main/java/com/google/gson/stream/JsonWriter.java b/gson/src/main/java/com/google/gson/stream/JsonWriter.java index 90e3529c..5afa228e 100644 --- a/gson/src/main/java/com/google/gson/stream/JsonWriter.java +++ b/gson/src/main/java/com/google/gson/stream/JsonWriter.java @@ -36,6 +36,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.regex.Pattern; +import com.google.gson.FormattingStyle; + /** * Writes a JSON (RFC 7159) * encoded value to a stream, one token at a time. The stream includes both @@ -180,10 +182,9 @@ public class JsonWriter implements Closeable, Flushable { } /** - * A string containing a full set of spaces for a single level of - * indentation, or null for no pretty printing. + * The settings used for pretty printing, or null for no pretty printing. */ - private String indent; + private FormattingStyle formattingStyle; /** * The name/value separator; either ":" or ": ". @@ -217,14 +218,44 @@ public class JsonWriter implements Closeable, Flushable { */ public final void setIndent(String indent) { if (indent.length() == 0) { - this.indent = null; + setFormattingStyle(null); + } else { + setFormattingStyle(FormattingStyle.DEFAULT.withIndent(indent)); + } + } + + /** + * Sets the pretty printing style to be used in the encoded document. + * No pretty printing if null. + * + *

Sets the various attributes to be used in the encoded document. + * For example the indentation string to be repeated for each level of indentation. + * Or the newline style, to accommodate various OS styles.

+ * + *

Has no effect if the serialized format is a single line.

+ * + * @param formattingStyle the style used for pretty printing, no pretty printing if null. + * @since $next-version$ + */ + public final void setFormattingStyle(FormattingStyle formattingStyle) { + this.formattingStyle = formattingStyle; + if (formattingStyle == null) { this.separator = ":"; } else { - this.indent = indent; this.separator = ": "; } } + /** + * Returns the pretty printing style used by this writer. + * + * @return the FormattingStyle that will be used. + * @since $next-version$ + */ + public final FormattingStyle getFormattingStyle() { + return formattingStyle; + } + /** * Configure this writer to relax its syntax rules. By default, this writer * only emits well-formed JSON as specified by >(), true, false, true, false, - true, true, false, true, LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, + FormattingStyle.DEFAULT, true, false, true, + LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, DateFormat.DEFAULT, new ArrayList(), new ArrayList(), new ArrayList(), CUSTOM_OBJECT_TO_NUMBER_STRATEGY, CUSTOM_NUMBER_TO_NUMBER_STRATEGY, @@ -79,7 +80,8 @@ public final class GsonTest { public void testClonedTypeAdapterFactoryListsAreIndependent() { Gson original = new Gson(CUSTOM_EXCLUDER, CUSTOM_FIELD_NAMING_STRATEGY, new HashMap>(), true, false, true, false, - true, true, false, true, LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, + FormattingStyle.DEFAULT, true, false, true, + LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, DateFormat.DEFAULT, new ArrayList(), new ArrayList(), new ArrayList(), CUSTOM_OBJECT_TO_NUMBER_STRATEGY, CUSTOM_NUMBER_TO_NUMBER_STRATEGY, diff --git a/gson/src/test/java/com/google/gson/functional/FormattingStyleTest.java b/gson/src/test/java/com/google/gson/functional/FormattingStyleTest.java new file mode 100644 index 00000000..4bdbb95f --- /dev/null +++ b/gson/src/test/java/com/google/gson/functional/FormattingStyleTest.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2022 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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 com.google.gson.functional; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import com.google.gson.FormattingStyle; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Functional tests for formatting styles. + * + * @author Mihai Nita + */ +@RunWith(JUnit4.class) +public class FormattingStyleTest { + + private static final String[] INPUT = {"v1", "v2"}; + private static final String EXPECTED = "[\"v1\",\"v2\"]"; + private static final String EXPECTED_OS = buildExpected(System.lineSeparator(), " "); + private static final String EXPECTED_CR = buildExpected("\r", " "); + private static final String EXPECTED_LF = buildExpected("\n", " "); + private static final String EXPECTED_CRLF = buildExpected("\r\n", " "); + + // Various valid strings that can be used for newline and indent + private static final String[] TEST_NEWLINES = { + "", "\r", "\n", "\r\n", "\n\r\r\n", System.lineSeparator() + }; + private static final String[] TEST_INDENTS = { + "", " ", " ", " ", "\t", " \t \t" + }; + + @Test + public void testDefault() { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + String json = gson.toJson(INPUT); + // Make sure the default uses LF, like before. + assertEquals(EXPECTED_LF, json); + } + + @Test + public void testNewlineCrLf() { + FormattingStyle style = FormattingStyle.DEFAULT.withNewline("\r\n"); + Gson gson = new GsonBuilder().setPrettyPrinting(style).create(); + String json = gson.toJson(INPUT); + assertEquals(EXPECTED_CRLF, json); + } + + @Test + public void testNewlineLf() { + FormattingStyle style = FormattingStyle.DEFAULT.withNewline("\n"); + Gson gson = new GsonBuilder().setPrettyPrinting(style).create(); + String json = gson.toJson(INPUT); + assertEquals(EXPECTED_LF, json); + } + + @Test + public void testNewlineCr() { + FormattingStyle style = FormattingStyle.DEFAULT.withNewline("\r"); + Gson gson = new GsonBuilder().setPrettyPrinting(style).create(); + String json = gson.toJson(INPUT); + assertEquals(EXPECTED_CR, json); + } + + @Test + public void testNewlineOs() { + FormattingStyle style = FormattingStyle.DEFAULT.withNewline(System.lineSeparator()); + Gson gson = new GsonBuilder().setPrettyPrinting(style).create(); + String json = gson.toJson(INPUT); + assertEquals(EXPECTED_OS, json); + } + + @Test + public void testVariousCombinationsToString() { + for (String indent : TEST_INDENTS) { + for (String newline : TEST_NEWLINES) { + FormattingStyle style = FormattingStyle.DEFAULT.withNewline(newline).withIndent(indent); + Gson gson = new GsonBuilder().setPrettyPrinting(style).create(); + String json = gson.toJson(INPUT); + assertEquals(buildExpected(newline, indent), json); + } + } + } + + @Test + public void testVariousCombinationsParse() { + // Mixing various indent and newline styles in the same string, to be parsed. + String jsonStringMix = "[\r\t'v1',\r\n 'v2'\n]"; + + String[] actualParsed; + // Test all that all combinations of newline can be parsed and generate the same INPUT. + for (String indent : TEST_INDENTS) { + for (String newline : TEST_NEWLINES) { + FormattingStyle style = FormattingStyle.DEFAULT.withNewline(newline).withIndent(indent); + Gson gson = new GsonBuilder().setPrettyPrinting(style).create(); + + String toParse = buildExpected(newline, indent); + actualParsed = gson.fromJson(toParse, INPUT.getClass()); + assertArrayEquals(INPUT, actualParsed); + + // Parse the mixed string with the gson parsers configured with various newline / indents. + actualParsed = gson.fromJson(jsonStringMix, INPUT.getClass()); + assertArrayEquals(INPUT, actualParsed); + } + } + } + + @Test + public void testStyleValidations() { + try { + // TBD if we want to accept \u2028 and \u2029. For now we don't. + FormattingStyle.DEFAULT.withNewline("\u2028"); + fail("Gson should not accept anything but \\r and \\n for newline"); + } catch (IllegalArgumentException expected) { + } + + try { + FormattingStyle.DEFAULT.withNewline("NL"); + fail("Gson should not accept anything but \\r and \\n for newline"); + } catch (IllegalArgumentException expected) { + } + + try { + FormattingStyle.DEFAULT.withIndent("\f"); + fail("Gson should not accept anything but space and tab for indent"); + } catch (IllegalArgumentException expected) { + } + } + + private static String buildExpected(String newline, String indent) { + return EXPECTED.replace("", newline).replace("", indent); + } +} diff --git a/gson/src/test/java/com/google/gson/internal/bind/JsonTreeWriterTest.java b/gson/src/test/java/com/google/gson/internal/bind/JsonTreeWriterTest.java index 3801033a..116d275c 100644 --- a/gson/src/test/java/com/google/gson/internal/bind/JsonTreeWriterTest.java +++ b/gson/src/test/java/com/google/gson/internal/bind/JsonTreeWriterTest.java @@ -278,8 +278,12 @@ public final class JsonTreeWriterTest { */ @Test public void testOverrides() { - List ignoredMethods = Arrays.asList("setLenient(boolean)", "isLenient()", "setIndent(java.lang.String)", - "setHtmlSafe(boolean)", "isHtmlSafe()", "setSerializeNulls(boolean)", "getSerializeNulls()"); + List ignoredMethods = Arrays.asList( + "setLenient(boolean)", "isLenient()", + "setIndent(java.lang.String)", + "setHtmlSafe(boolean)", "isHtmlSafe()", + "setFormattingStyle(com.google.gson.FormattingStyle)", "getFormattingStyle()", + "setSerializeNulls(boolean)", "getSerializeNulls()"); MoreAsserts.assertOverridesMethods(JsonWriter.class, JsonTreeWriter.class, ignoredMethods); } } diff --git a/gson/src/test/java/com/google/gson/stream/JsonWriterTest.java b/gson/src/test/java/com/google/gson/stream/JsonWriterTest.java index a0896676..70470a16 100644 --- a/gson/src/test/java/com/google/gson/stream/JsonWriterTest.java +++ b/gson/src/test/java/com/google/gson/stream/JsonWriterTest.java @@ -19,6 +19,7 @@ package com.google.gson.stream; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; +import com.google.gson.FormattingStyle; import com.google.gson.internal.LazilyParsedNumber; import java.io.IOException; import java.io.StringWriter; @@ -844,4 +845,30 @@ public final class JsonWriterTest { writer.close(); writer.close(); } + + @Test + public void testSetGetFormattingStyle() throws IOException { + String lineSeparator = "\r\n"; + + StringWriter stringWriter = new StringWriter(); + JsonWriter jsonWriter = new JsonWriter(stringWriter); + jsonWriter.setFormattingStyle(FormattingStyle.DEFAULT.withIndent(" \t ").withNewline(lineSeparator)); + + jsonWriter.beginArray(); + jsonWriter.value(true); + jsonWriter.value("text"); + jsonWriter.value(5.0); + jsonWriter.nullValue(); + jsonWriter.endArray(); + + String expected = "[\r\n" + + " \t true,\r\n" + + " \t \"text\",\r\n" + + " \t 5.0,\r\n" + + " \t null\r\n" + + "]"; + assertThat(stringWriter.toString()).isEqualTo(expected); + + assertThat(jsonWriter.getFormattingStyle().getNewline()).isEqualTo(lineSeparator); + } }