Skip to content
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

Fixes #776: Add settings for the kind of newline to use #2231

Merged
merged 21 commits into from
Feb 12, 2023
Merged
Show file tree
Hide file tree
Changes from 19 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
69 changes: 69 additions & 0 deletions gson/src/main/java/com/google/gson/FormattingStyle.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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;

/**
* An enumeration that defines the kind of newline to use for serialization.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this comment needs to be rewritten slightly? It isn't an enumeration, for example.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

*
* @see <a href="https://en.wikipedia.org/wiki/Newline">Wikipedia Newline article</a>
*
* @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 \\n and \\r are allowed in newline.");
}
if (!indent.matches("[ \t]*")) {
throw new IllegalArgumentException("Only spaces and tabs allowed in indent.");
}
this.newline = newline;
this.indent = indent;
}

public FormattingStyle withNewline(String newline) {
return new FormattingStyle(newline, this.indent);
}

public FormattingStyle withIndent(String indent) {
return new FormattingStyle(this.newline, indent);
}

/**
* The string value that will be used as newline.
*
* @return the newline value.
*/
public String getNewline() {
return this.newline;
}

public String getIndent() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a javadoc comment here too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

return this.indent;
}
}
31 changes: 16 additions & 15 deletions gson/src/main/java/com/google/gson/Gson.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@
*
* <p>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.</p>
* configuration options such as versioning support, pretty printing, custom newline, custom indent,
* custom {@link JsonSerializer}s, {@link JsonDeserializer}s, and {@link InstanceCreator}s.</p>
*
* <p>Here is an example of how Gson is used for a simple Class:
*
Expand Down Expand Up @@ -141,7 +141,7 @@
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;
Expand Down Expand Up @@ -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;
Expand All @@ -202,7 +202,9 @@ public final class Gson {
* <ul>
* <li>The JSON generated by <code>toJson</code> methods is in compact representation. This
* means that all the unneeded white-space is removed. You can change this behavior with
* {@link GsonBuilder#setPrettyPrinting()}. </li>
* {@link GsonBuilder#setPrettyPrinting()}.</li>
* <li>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)}.</li>
* <li>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
Expand Down Expand Up @@ -234,7 +236,7 @@ public Gson() {
this(Excluder.DEFAULT, DEFAULT_FIELD_NAMING_STRATEGY,
Collections.<Type, InstanceCreator<?>>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.<TypeAdapterFactory>emptyList(), Collections.<TypeAdapterFactory>emptyList(),
Expand All @@ -245,7 +247,7 @@ public Gson() {
Gson(Excluder excluder, FieldNamingStrategy fieldNamingStrategy,
Map<Type, InstanceCreator<?>> 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<TypeAdapterFactory> builderFactories,
Expand All @@ -261,7 +263,7 @@ public 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;
Expand Down Expand Up @@ -702,7 +704,7 @@ public JsonElement toJsonTree(Object src) {
* <pre>
* Type typeOfSrc = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
* </pre>
* @return Json representation of {@code src}
* @return JSON representation of {@code src}.
* @since 1.4
*
* @see #toJsonTree(Object)
Expand All @@ -724,7 +726,7 @@ public JsonElement toJsonTree(Object src, Type typeOfSrc) {
* {@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)
Expand All @@ -749,7 +751,7 @@ public String toJson(Object src) {
* <pre>
* Type typeOfSrc = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
* </pre>
* @return JSON representation of {@code src}
* @return JSON representation of {@code src}.
*
* @see #toJson(Object, Type, Appendable)
* @see #toJson(Object)
Expand Down Expand Up @@ -855,7 +857,7 @@ public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOE
* 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) {
Expand Down Expand Up @@ -891,16 +893,15 @@ public void toJson(JsonElement jsonElement, Appendable writer) throws JsonIOExce
* <li>{@link GsonBuilder#serializeNulls()}</li>
* <li>{@link GsonBuilder#setLenient()}</li>
* <li>{@link GsonBuilder#setPrettyPrinting()}</li>
* <li>{@link GsonBuilder#setPrettyPrinting(FormattingStyle)}</li>
* </ul>
*/
public JsonWriter newJsonWriter(Writer writer) throws IOException {
if (generateNonExecutableJson) {
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);
Expand Down
27 changes: 20 additions & 7 deletions gson/src/main/java/com/google/gson/GsonBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -129,7 +129,7 @@ public 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;
Expand Down Expand Up @@ -478,13 +478,26 @@ public GsonBuilder addDeserializationExclusionStrategy(ExclusionStrategy strateg
}

/**
* 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.
mihnita marked this conversation as resolved.
Show resolved Hide resolved
*
* <p>Has no effect if the serialized format is a single line.</p>
*
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
* @since $next-version$
*/
mihnita marked this conversation as resolved.
Show resolved Hide resolved
public GsonBuilder setPrettyPrinting(FormattingStyle formattingStyle) {
this.formattingStyle = formattingStyle;
return this;
}

Expand Down Expand Up @@ -761,7 +774,7 @@ public Gson create() {

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,
Expand Down
2 changes: 1 addition & 1 deletion gson/src/main/java/com/google/gson/JsonPrimitive.java
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public boolean getAsBoolean() {
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());
}

Expand Down
47 changes: 39 additions & 8 deletions gson/src/main/java/com/google/gson/stream/JsonWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Pattern;

import com.google.gson.FormattingStyle;

/**
* Writes a JSON (<a href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>)
* encoded value to a stream, one token at a time. The stream includes both
Expand Down Expand Up @@ -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 ": ".
Expand Down Expand Up @@ -217,14 +218,44 @@ public JsonWriter(Writer out) {
*/
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.
*
* <p>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.</p>
*
* <p>Has no effect if the serialized format is a single line.</p>
*
* @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 <a
Expand Down Expand Up @@ -645,13 +676,13 @@ private void string(String value) throws IOException {
}

private void newline() throws IOException {
if (indent == null) {
if (formattingStyle == null) {
return;
}

out.write('\n');
out.write(formattingStyle.getNewline());
for (int i = 1, size = stackSize; i < size; i++) {
out.write(indent);
out.write(formattingStyle.getIndent());
}
}

Expand Down
16 changes: 8 additions & 8 deletions gson/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@
* @since 2.8.6
*/
module com.google.gson {
exports com.google.gson;
exports com.google.gson.annotations;
exports com.google.gson.reflect;
exports com.google.gson.stream;
exports com.google.gson;
exports com.google.gson.annotations;
exports com.google.gson.reflect;
exports com.google.gson.stream;

// Optional dependency on java.sql
requires static java.sql;
// Optional dependency on java.sql
requires static java.sql;

// Optional dependency on jdk.unsupported for JDK's sun.misc.Unsafe
requires static jdk.unsupported;
// Optional dependency on jdk.unsupported for JDK's sun.misc.Unsafe
requires static jdk.unsupported;
}
6 changes: 4 additions & 2 deletions gson/src/test/java/com/google/gson/GsonTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ public final class GsonTest {
public void testOverridesDefaultExcluder() {
Gson gson = new Gson(CUSTOM_EXCLUDER, CUSTOM_FIELD_NAMING_STRATEGY,
new HashMap<Type, InstanceCreator<?>>(), 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<TypeAdapterFactory>(),
new ArrayList<TypeAdapterFactory>(), new ArrayList<TypeAdapterFactory>(),
CUSTOM_OBJECT_TO_NUMBER_STRATEGY, CUSTOM_NUMBER_TO_NUMBER_STRATEGY,
Expand All @@ -79,7 +80,8 @@ public void testOverridesDefaultExcluder() {
public void testClonedTypeAdapterFactoryListsAreIndependent() {
Gson original = new Gson(CUSTOM_EXCLUDER, CUSTOM_FIELD_NAMING_STRATEGY,
new HashMap<Type, InstanceCreator<?>>(), 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<TypeAdapterFactory>(),
new ArrayList<TypeAdapterFactory>(), new ArrayList<TypeAdapterFactory>(),
CUSTOM_OBJECT_TO_NUMBER_STRATEGY, CUSTOM_NUMBER_TO_NUMBER_STRATEGY,
Expand Down
Loading