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

Guarantee that JsonElement.toString() produces JSON #2659

Merged
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
3 changes: 3 additions & 0 deletions gson/src/main/java/com/google/gson/JsonArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
* <p>{@code JsonArray} only implements the {@link Iterable} interface but not the {@link List}
* interface. A {@code List} view of it can be obtained with {@link #asList()}.
*
* <p>See the {@link JsonElement} documentation for details on how to convert {@code JsonArray} and
* generally any {@code JsonElement} from and to JSON.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
Expand Down
100 changes: 99 additions & 1 deletion gson/src/main/java/com/google/gson/JsonElement.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.google.gson.internal.Streams;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.math.BigInteger;
Expand All @@ -28,6 +29,69 @@
* A class representing an element of JSON. It could either be a {@link JsonObject}, a {@link
* JsonArray}, a {@link JsonPrimitive} or a {@link JsonNull}.
*
* <p>This class provides multiple {@code getAs} methods which allow
*
* <ul>
* <li>obtaining the represented primitive value, for example {@link #getAsString()}
* <li>casting to the {@code JsonElement} subclasses in a convenient way, for example {@link
* #getAsJsonObject()}
* </ul>
*
* <h2>Converting {@code JsonElement} from / to JSON</h2>
*
* There are two ways to parse JSON data as a {@link JsonElement}:
*
* <ul>
* <li>{@link JsonParser}, for example:
* <pre>
* JsonObject jsonObject = JsonParser.parseString("{}").getAsJsonObject();
* </pre>
* <li>{@link Gson#fromJson(Reader, Class) Gson.fromJson(..., JsonElement.class)}<br>
* It is possible to directly specify a {@code JsonElement} subclass, for example:
* <pre>
* JsonObject jsonObject = gson.fromJson("{}", JsonObject.class);
* </pre>
* </ul>
*
* To convert a {@code JsonElement} to JSON either {@link #toString() JsonElement.toString()} or the
* method {@link Gson#toJson(JsonElement)} and its overloads can be used.
*
* <p>It is also possible to obtain the {@link TypeAdapter} for {@code JsonElement} from a {@link
* Gson} instance and then use it for conversion from and to JSON:
*
* <pre>{@code
* TypeAdapter<JsonElement> adapter = gson.getAdapter(JsonElement.class);
*
* JsonElement value = adapter.fromJson("{}");
* String json = adapter.toJson(value);
* }</pre>
*
* <h2>{@code JsonElement} as JSON data</h2>
*
* {@code JsonElement} can also be treated as JSON data, allowing to deserialize from a {@code
* JsonElement} and serializing to a {@code JsonElement}. The {@link Gson} class offers these
* methods for this:
*
* <ul>
* <li>{@link Gson#fromJson(JsonElement, Class) Gson.fromJson(JsonElement, ...)}, for example:
* <pre>
* JsonObject jsonObject = ...;
* MyClass myObj = gson.fromJson(jsonObject, MyClass.class);
* </pre>
* <li>{@link Gson#toJsonTree(Object)}, for example:
* <pre>
* MyClass myObj = ...;
* JsonElement json = gson.toJsonTree(myObj);
* </pre>
* </ul>
*
* The {@link TypeAdapter} class provides corresponding methods as well:
*
* <ul>
* <li>{@link TypeAdapter#fromJsonTree(JsonElement)}
* <li>{@link TypeAdapter#toJsonTree(Object)}
* </ul>
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
Expand Down Expand Up @@ -320,7 +384,41 @@ public short getAsShort() {
throw new UnsupportedOperationException(getClass().getSimpleName());
}

/** Returns a String representation of this element. */
/**
* Converts this element to a JSON string.
*
* <p>For example:
*
* <pre>
* JsonObject object = new JsonObject();
* object.add("a", JsonNull.INSTANCE);
* JsonArray array = new JsonArray();
* array.add(1);
* object.add("b", array);
*
* String json = object.toString();
* // json: {"a":null,"b":[1]}
* </pre>
*
* If this element or any nested elements contain {@link Double#NaN NaN} or {@link
* Double#isInfinite() Infinity} that value is written to JSON, even though the JSON specification
* does not permit these values.
*
* <p>To customize formatting or to directly write to an {@link Appendable} instead of creating an
* intermediate {@code String} first, use {@link Gson#toJson(JsonElement, Appendable)
* Gson.toJson(JsonElement, ...)}.
*
* <p>To get the contained String value (without enclosing {@code "} and without escaping), use
* {@link #getAsString()} instead:
*
* <pre>
* JsonPrimitive jsonPrimitive = new JsonPrimitive("with \" quote");
* String json = jsonPrimitive.toString();
* // json: "with \" quote"
* String value = jsonPrimitive.getAsString();
* // value: with " quote
* </pre>
*/
@Override
public String toString() {
try {
Expand Down
5 changes: 4 additions & 1 deletion gson/src/main/java/com/google/gson/JsonObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import java.util.Set;

/**
* A class representing an object type in Json. An object consists of name-value pairs where names
* A class representing an object type in JSON. An object consists of name-value pairs where names
* are strings, and values are any other type of {@link JsonElement}. This allows for a creating a
* tree of JsonElements. The member elements of this object are maintained in order they were added.
* This class does not support {@code null} values. If {@code null} is provided as value argument to
Expand All @@ -31,6 +31,9 @@
* <p>{@code JsonObject} does not implement the {@link Map} interface, but a {@code Map} view of it
* can be obtained with {@link #asMap()}.
*
* <p>See the {@link JsonElement} documentation for details on how to convert {@code JsonObject} and
* generally any {@code JsonElement} from and to JSON.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
Expand Down
2 changes: 1 addition & 1 deletion gson/src/main/java/com/google/gson/JsonParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
* following example demonstrates how to achieve it:
*
* <pre>
* String json = "{\"skipObj\": {\"skipKey\": \"skipValue\"},\"obj\": {\"key\": \"value\"}}";
* String json = "{\"skipObj\": {\"skipKey\": \"skipValue\"}, \"obj\": {\"key\": \"value\"}}";
* try (JsonReader jsonReader = new JsonReader(new StringReader(json))) {
* jsonReader.beginObject();
* while (jsonReader.hasNext()) {
Expand Down
3 changes: 3 additions & 0 deletions gson/src/main/java/com/google/gson/JsonPrimitive.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
* A class representing a JSON primitive value. A primitive value is either a String, a Java
* primitive, or a Java primitive wrapper type.
*
* <p>See the {@link JsonElement} documentation for details on how to convert {@code JsonPrimitive}
* and generally any {@code JsonElement} from and to JSON.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
Expand Down
17 changes: 17 additions & 0 deletions gson/src/test/java/com/google/gson/JsonArrayTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -376,4 +376,21 @@ public void testSameAddition() {
assertThat(jsonArray.toString())
.isEqualTo("[\"a\",\"a\",true,true,1212,1212,34.34,34.34,null,null]");
}

@Test
public void testToString() {
JsonArray array = new JsonArray();
assertThat(array.toString()).isEqualTo("[]");

array.add(JsonNull.INSTANCE);
array.add(Float.NaN);
array.add("a\0");
JsonArray nestedArray = new JsonArray();
nestedArray.add('"');
array.add(nestedArray);
JsonObject nestedObject = new JsonObject();
nestedObject.addProperty("n\0", 1);
array.add(nestedObject);
assertThat(array.toString()).isEqualTo("[null,NaN,\"a\\u0000\",[\"\\\"\"],{\"n\\u0000\":1}]");
}
}
5 changes: 5 additions & 0 deletions gson/src/test/java/com/google/gson/JsonNullTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,9 @@ public void testDeepCopy() {
assertThat(a.deepCopy()).isSameInstanceAs(JsonNull.INSTANCE);
assertThat(JsonNull.INSTANCE.deepCopy()).isSameInstanceAs(JsonNull.INSTANCE);
}

@Test
public void testToString() {
assertThat(JsonNull.INSTANCE.toString()).isEqualTo("null");
}
}
17 changes: 17 additions & 0 deletions gson/src/test/java/com/google/gson/JsonObjectTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -344,4 +344,21 @@ public void testEntrySet() {
assertThat(new ArrayList<>(o.entrySet())).isEqualTo(new ArrayList<>(expectedEntriesQueue));
}
}

@Test
public void testToString() {
JsonObject object = new JsonObject();
assertThat(object.toString()).isEqualTo("{}");

object.add("a", JsonNull.INSTANCE);
object.addProperty("b\0", Float.NaN);
JsonArray nestedArray = new JsonArray();
nestedArray.add('"');
object.add("c", nestedArray);
JsonObject nestedObject = new JsonObject();
nestedObject.addProperty("n\0", 1);
object.add("d", nestedObject);
assertThat(object.toString())
.isEqualTo("{\"a\":null,\"b\\u0000\":NaN,\"c\":[\"\\\"\"],\"d\":{\"n\\u0000\":1}}");
}
}
24 changes: 23 additions & 1 deletion gson/src/test/java/com/google/gson/JsonPrimitiveTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -260,12 +260,34 @@ public void testDoubleEqualsBigDecimal() {
}

@Test
public void testValidJsonOnToString() {
public void testToString() {
JsonPrimitive json = new JsonPrimitive("Some\nEscaped\nValue");
assertThat(json.toString()).isEqualTo("\"Some\\nEscaped\\nValue\"");

json = new JsonPrimitive("");
assertThat(json.toString()).isEqualTo("\"\"");

json = new JsonPrimitive(new BigDecimal("1.333"));
assertThat(json.toString()).isEqualTo("1.333");

// Preserves trailing 0
json = new JsonPrimitive(new BigDecimal("1.0000"));
assertThat(json.toString()).isEqualTo("1.0000");

json = new JsonPrimitive(Float.NaN);
assertThat(json.toString()).isEqualTo("NaN");

json = new JsonPrimitive(Double.NEGATIVE_INFINITY);
assertThat(json.toString()).isEqualTo("-Infinity");

json = new JsonPrimitive('a');
assertThat(json.toString()).isEqualTo("\"a\"");

json = new JsonPrimitive('\0');
assertThat(json.toString()).isEqualTo("\"\\u0000\"");

json = new JsonPrimitive(true);
assertThat(json.toString()).isEqualTo("true");
}

@Test
Expand Down