Ensure that the JsonElement.toString() will always generate valid JSON (even when control characters/whitespace characters are used).
This commit is contained in:
parent
3b0f8f4340
commit
50eb582657
@ -87,7 +87,6 @@ public final class Gson {
|
||||
|
||||
private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n";
|
||||
|
||||
|
||||
private final ExclusionStrategy serializationStrategy;
|
||||
|
||||
private final ExclusionStrategy deserializationStrategy;
|
||||
|
@ -293,7 +293,7 @@ public final class JsonArray extends JsonElement implements Iterable<JsonElement
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toString(Appendable sb) throws IOException {
|
||||
protected void toString(Appendable sb, Escaper escaper) throws IOException {
|
||||
sb.append('[');
|
||||
boolean first = true;
|
||||
for (JsonElement element : elements) {
|
||||
@ -302,7 +302,7 @@ public final class JsonArray extends JsonElement implements Iterable<JsonElement
|
||||
} else {
|
||||
sb.append(',');
|
||||
}
|
||||
element.toString(sb);
|
||||
element.toString(sb, escaper);
|
||||
}
|
||||
sb.append(']');
|
||||
}
|
||||
|
@ -27,15 +27,17 @@ final class JsonCompactFormatter implements JsonFormatter {
|
||||
|
||||
private static class FormattingVisitor implements JsonElementVisitor {
|
||||
private final Appendable writer;
|
||||
private final Escaper escaper;
|
||||
private final boolean serializeNulls;
|
||||
|
||||
FormattingVisitor(Appendable writer, boolean serializeNulls) {
|
||||
FormattingVisitor(Appendable writer, Escaper escaper, boolean serializeNulls) {
|
||||
this.writer = writer;
|
||||
this.escaper = escaper;
|
||||
this.serializeNulls = serializeNulls;
|
||||
}
|
||||
|
||||
public void visitPrimitive(JsonPrimitive primitive) throws IOException {
|
||||
primitive.toString(writer);
|
||||
primitive.toString(writer, escaper);
|
||||
}
|
||||
|
||||
public void visitNull() throws IOException {
|
||||
@ -51,7 +53,7 @@ final class JsonCompactFormatter implements JsonFormatter {
|
||||
if (!isFirst) {
|
||||
writer.append(',');
|
||||
}
|
||||
member.toString(writer);
|
||||
member.toString(writer, escaper);
|
||||
}
|
||||
|
||||
public void visitArrayMember(JsonArray parent, JsonArray member,
|
||||
@ -90,7 +92,7 @@ final class JsonCompactFormatter implements JsonFormatter {
|
||||
writer.append('"');
|
||||
writer.append(memberName);
|
||||
writer.append("\":");
|
||||
member.toString(writer);
|
||||
member.toString(writer, escaper);
|
||||
}
|
||||
|
||||
public void visitObjectMember(JsonObject parent, String memberName, JsonArray member,
|
||||
@ -140,8 +142,8 @@ final class JsonCompactFormatter implements JsonFormatter {
|
||||
if (root == null) {
|
||||
return;
|
||||
}
|
||||
JsonElementVisitor visitor = new JsonEscapingVisitor(
|
||||
new FormattingVisitor(writer, serializeNulls), escapeHtmlChars);
|
||||
JsonElementVisitor visitor = new FormattingVisitor(
|
||||
writer, new Escaper(escapeHtmlChars), serializeNulls);
|
||||
JsonTreeNavigator navigator = new JsonTreeNavigator(visitor, serializeNulls);
|
||||
navigator.navigate(root);
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import java.math.BigInteger;
|
||||
* @author Joel Leitch
|
||||
*/
|
||||
public abstract class JsonElement {
|
||||
private static final Escaper BASIC_ESCAPER = new Escaper(false);
|
||||
|
||||
/**
|
||||
* provides check for verifying if this element is an array or not.
|
||||
@ -330,12 +331,12 @@ public abstract class JsonElement {
|
||||
public String toString() {
|
||||
try {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
toString(sb);
|
||||
toString(sb, BASIC_ESCAPER);
|
||||
return sb.toString();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void toString(Appendable sb) throws IOException;
|
||||
protected abstract void toString(Appendable sb, Escaper escaper) throws IOException;
|
||||
}
|
||||
|
@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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.io.IOException;
|
||||
|
||||
/**
|
||||
* Performs JSON escaping and passes on the new escaped value to the delegate
|
||||
* {@link JsonElementVisitor}.
|
||||
*
|
||||
* @author Joel Leitch
|
||||
*/
|
||||
class JsonEscapingVisitor extends DelegatingJsonElementVisitor {
|
||||
private final Escaper escaper;
|
||||
|
||||
/**
|
||||
* Constructs a Visitor that will properly escape any JSON primitive values.
|
||||
*
|
||||
* @param delegate the JsonElementVisitor that this instance will use for delegation
|
||||
*/
|
||||
protected JsonEscapingVisitor(JsonElementVisitor delegate, boolean escapeHtmlChars) {
|
||||
this(delegate, new Escaper(escapeHtmlChars));
|
||||
}
|
||||
|
||||
protected JsonEscapingVisitor(JsonElementVisitor delegate, Escaper escaper) {
|
||||
super(delegate);
|
||||
this.escaper = escaper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitArrayMember(JsonArray parent, JsonPrimitive member,
|
||||
boolean isFirst) throws IOException {
|
||||
super.visitArrayMember(parent, escapeJsonPrimitive(member), isFirst);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitObjectMember(JsonObject parent, String memberName, JsonPrimitive member,
|
||||
boolean isFirst) throws IOException {
|
||||
super.visitObjectMember(parent, memberName, escapeJsonPrimitive(member), isFirst);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPrimitive(JsonPrimitive primitive) throws IOException {
|
||||
super.visitPrimitive(escapeJsonPrimitive(primitive));
|
||||
}
|
||||
|
||||
private JsonPrimitive escapeJsonPrimitive(JsonPrimitive member) {
|
||||
if (member.isString()) {
|
||||
String memberValue = member.getAsString();
|
||||
member.setValue(escaper.escapeJsonString(memberValue));
|
||||
}
|
||||
return member;
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@ public final class JsonNull extends JsonElement {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toString(Appendable sb) throws IOException {
|
||||
protected void toString(Appendable sb, Escaper escaper) throws IOException {
|
||||
sb.append("null");
|
||||
}
|
||||
|
||||
|
@ -191,7 +191,7 @@ public final class JsonObject extends JsonElement {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toString(Appendable sb) throws IOException {
|
||||
protected void toString(Appendable sb, Escaper escaper) throws IOException {
|
||||
sb.append('{');
|
||||
boolean first = true;
|
||||
for (Map.Entry<String, JsonElement> entry : members.entrySet()) {
|
||||
@ -203,7 +203,7 @@ public final class JsonObject extends JsonElement {
|
||||
sb.append('\"');
|
||||
sb.append(entry.getKey());
|
||||
sb.append("\":");
|
||||
entry.getValue().toString(sb);
|
||||
entry.getValue().toString(sb, escaper);
|
||||
}
|
||||
sb.append('}');
|
||||
}
|
||||
|
@ -29,13 +29,11 @@ import java.math.BigInteger;
|
||||
* @author Joel Leitch
|
||||
*/
|
||||
public final class JsonPrimitive extends JsonElement {
|
||||
|
||||
private static final Class<?>[] PRIMITIVE_TYPES = { int.class, long.class, short.class,
|
||||
float.class, double.class, byte.class, boolean.class, char.class, Integer.class, Long.class,
|
||||
Short.class, Float.class, Double.class, Byte.class, Boolean.class, Character.class };
|
||||
|
||||
private static final BigInteger INTEGER_MAX = BigInteger.valueOf(Integer.MAX_VALUE);
|
||||
|
||||
private static final BigInteger LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE);
|
||||
|
||||
private Object value;
|
||||
@ -320,10 +318,10 @@ public final class JsonPrimitive extends JsonElement {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void toString(Appendable sb) throws IOException {
|
||||
if (value instanceof String) {
|
||||
protected void toString(Appendable sb, Escaper escaper) throws IOException {
|
||||
if (isString()) {
|
||||
sb.append('"');
|
||||
sb.append((String) value);
|
||||
sb.append(escaper.escapeJsonString(value.toString()));
|
||||
sb.append('"');
|
||||
} else {
|
||||
sb.append(value.toString());
|
||||
|
@ -146,11 +146,13 @@ final class JsonPrintFormatter implements JsonFormatter {
|
||||
private final Map<Integer, Boolean> firstArrayElement;
|
||||
private final Map<Integer, Boolean> firstObjectMember;
|
||||
private final JsonWriter writer;
|
||||
private final Escaper escaper;
|
||||
private final boolean serializeNulls;
|
||||
private int level = 0;
|
||||
|
||||
PrintFormattingVisitor(JsonWriter writer, boolean serializeNulls) {
|
||||
PrintFormattingVisitor(JsonWriter writer, Escaper escaper, boolean serializeNulls) {
|
||||
this.writer = writer;
|
||||
this.escaper = escaper;
|
||||
this.serializeNulls = serializeNulls;
|
||||
this.firstArrayElement = new HashMap<Integer, Boolean>();
|
||||
this.firstObjectMember = new HashMap<Integer, Boolean>();
|
||||
@ -172,7 +174,7 @@ final class JsonPrintFormatter implements JsonFormatter {
|
||||
public void visitArrayMember(JsonArray parent, JsonPrimitive member,
|
||||
boolean isFirst) throws IOException {
|
||||
addCommaCheckingFirst(firstArrayElement);
|
||||
writer.value(member.toString());
|
||||
writer.value(escapeJsonPrimitive(member));
|
||||
}
|
||||
|
||||
public void visitArrayMember(JsonArray parent, JsonArray member,
|
||||
@ -204,7 +206,7 @@ final class JsonPrintFormatter implements JsonFormatter {
|
||||
addCommaCheckingFirst(firstObjectMember);
|
||||
writer.key(memberName);
|
||||
writer.fieldSeparator();
|
||||
writer.value(member.toString());
|
||||
writer.value(escapeJsonPrimitive(member));
|
||||
}
|
||||
|
||||
public void visitObjectMember(JsonObject parent, String memberName, JsonArray member,
|
||||
@ -233,12 +235,18 @@ final class JsonPrintFormatter implements JsonFormatter {
|
||||
}
|
||||
|
||||
public void visitPrimitive(JsonPrimitive primitive) throws IOException {
|
||||
writer.value(primitive.toString());
|
||||
writer.value(escapeJsonPrimitive(primitive));
|
||||
}
|
||||
|
||||
public void visitNull() throws IOException {
|
||||
writer.value("null");
|
||||
}
|
||||
|
||||
private String escapeJsonPrimitive(JsonPrimitive member) throws IOException {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
member.toString(builder, escaper);
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public void format(JsonElement root, Appendable writer,
|
||||
@ -247,8 +255,8 @@ final class JsonPrintFormatter implements JsonFormatter {
|
||||
return;
|
||||
}
|
||||
JsonWriter jsonWriter = new JsonWriter(writer);
|
||||
JsonElementVisitor visitor = new JsonEscapingVisitor(
|
||||
new PrintFormattingVisitor(jsonWriter, serializeNulls), escapeHtmlChars);
|
||||
JsonElementVisitor visitor = new PrintFormattingVisitor(
|
||||
jsonWriter, new Escaper(escapeHtmlChars), serializeNulls);
|
||||
JsonTreeNavigator navigator = new JsonTreeNavigator(visitor, serializeNulls);
|
||||
navigator.navigate(root);
|
||||
jsonWriter.finishLine();
|
||||
|
@ -1,182 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 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 junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Performs some unit testing for the {@link JsonEscapingVisitor} class.
|
||||
*
|
||||
* @author Joel Leitch
|
||||
*/
|
||||
public class JsonEscapingVisitorTest extends TestCase {
|
||||
private StubbedJsonElementVisitor stubVisitor;
|
||||
private JsonEscapingVisitor escapingVisitor;
|
||||
private Escaper escaper;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
stubVisitor = new StubbedJsonElementVisitor();
|
||||
escaper = new Escaper(true);
|
||||
escapingVisitor = new JsonEscapingVisitor(stubVisitor, escaper);
|
||||
}
|
||||
|
||||
public void testNonStringPrimitiveVisitation() throws Exception {
|
||||
boolean value = true;
|
||||
JsonPrimitive primitive = new JsonPrimitive(value);
|
||||
escapingVisitor.visitPrimitive(primitive);
|
||||
assertEquals(value, stubVisitor.primitiveReceived.getAsBoolean());
|
||||
}
|
||||
|
||||
public void testStringPrimitiveVisitationNoEscapingRequired() throws Exception {
|
||||
String value = "Testing123";
|
||||
JsonPrimitive primitive = new JsonPrimitive(value);
|
||||
escapingVisitor.visitPrimitive(primitive);
|
||||
assertEquals(value, stubVisitor.primitiveReceived.getAsObject());
|
||||
}
|
||||
|
||||
public void testStringPrimitiveVisitationEscapingRequired() throws Exception {
|
||||
String value = "Testing\"123";
|
||||
JsonPrimitive primitive = new JsonPrimitive(value);
|
||||
escapingVisitor.visitPrimitive(primitive);
|
||||
assertEquals(escaper.escapeJsonString(value), stubVisitor.primitiveReceived.getAsString());
|
||||
}
|
||||
|
||||
public void testNonStringArrayVisitation() throws Exception {
|
||||
int value = 123;
|
||||
JsonPrimitive primitive = new JsonPrimitive(value);
|
||||
JsonArray array = new JsonArray();
|
||||
array.add(primitive);
|
||||
escapingVisitor.visitArrayMember(array, primitive, true);
|
||||
assertEquals(value, stubVisitor.primitiveReceived.getAsInt());
|
||||
}
|
||||
|
||||
public void testStringArrayVisitationNoEscaping() throws Exception {
|
||||
String value = "Testing123";
|
||||
JsonPrimitive primitive = new JsonPrimitive(value);
|
||||
JsonArray array = new JsonArray();
|
||||
array.add(primitive);
|
||||
escapingVisitor.visitArrayMember(array, primitive, true);
|
||||
assertEquals(value, stubVisitor.primitiveReceived.getAsString());
|
||||
}
|
||||
|
||||
public void testStringArrayVisitationEscapingRequired() throws Exception {
|
||||
String value = "Testing\"123";
|
||||
JsonPrimitive primitive = new JsonPrimitive(value);
|
||||
JsonArray array = new JsonArray();
|
||||
array.add(primitive);
|
||||
escapingVisitor.visitArrayMember(array, primitive, true);
|
||||
assertEquals(escaper.escapeJsonString(value), stubVisitor.primitiveReceived.getAsString());
|
||||
}
|
||||
|
||||
public void testNonStringFieldVisitation() throws Exception {
|
||||
String fieldName = "fieldName";
|
||||
int value = 123;
|
||||
JsonPrimitive primitive = new JsonPrimitive(value);
|
||||
JsonObject object = new JsonObject();
|
||||
object.addProperty(fieldName, value);
|
||||
|
||||
escapingVisitor.visitObjectMember(object, fieldName, primitive, true);
|
||||
assertEquals(value, stubVisitor.primitiveReceived.getAsInt());
|
||||
}
|
||||
|
||||
public void testStringFieldVisitationNoEscaping() throws Exception {
|
||||
String fieldName = "fieldName";
|
||||
String value = "Testing123";
|
||||
JsonPrimitive primitive = new JsonPrimitive(value);
|
||||
JsonObject object = new JsonObject();
|
||||
object.addProperty(fieldName, value);
|
||||
|
||||
escapingVisitor.visitObjectMember(object, fieldName, primitive, true);
|
||||
assertEquals(value, stubVisitor.primitiveReceived.getAsString());
|
||||
}
|
||||
|
||||
public void testStringFieldVisitationEscapingRequired() throws Exception {
|
||||
String fieldName = "fieldName";
|
||||
String value = "Testing\"123";
|
||||
JsonPrimitive primitive = new JsonPrimitive(value);
|
||||
JsonObject object = new JsonObject();
|
||||
object.addProperty(fieldName, value);
|
||||
|
||||
escapingVisitor.visitObjectMember(object, fieldName, primitive, true);
|
||||
assertEquals(escaper.escapeJsonString(value), stubVisitor.primitiveReceived.getAsString());
|
||||
}
|
||||
|
||||
private static class StubbedJsonElementVisitor implements JsonElementVisitor {
|
||||
public JsonPrimitive primitiveReceived;
|
||||
|
||||
public void endArray(JsonArray array) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void endObject(JsonObject object) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void startArray(JsonArray array) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void startObject(JsonObject object) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void visitArrayMember(JsonArray parent, JsonPrimitive member, boolean isFirst) {
|
||||
primitiveReceived = member;
|
||||
}
|
||||
|
||||
public void visitArrayMember(JsonArray parent, JsonArray member, boolean isFirst) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void visitArrayMember(JsonArray parent, JsonObject member, boolean isFirst) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void visitObjectMember(
|
||||
JsonObject parent, String memberName, JsonPrimitive member, boolean isFirst) {
|
||||
primitiveReceived = member;
|
||||
}
|
||||
|
||||
public void visitObjectMember(
|
||||
JsonObject parent, String memberName, JsonArray member, boolean isFirst) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void visitObjectMember(
|
||||
JsonObject parent, String memberName, JsonObject member, boolean isFirst) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void visitPrimitive(JsonPrimitive primitive) {
|
||||
primitiveReceived = primitive;
|
||||
}
|
||||
|
||||
public void visitNullArrayMember(JsonArray parent, boolean isFirst) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void visitNull() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void visitNullObjectMember(JsonObject parent, String memberName, boolean isFirst) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
@ -80,4 +80,12 @@ public class JsonPrimitiveTest extends TestCase {
|
||||
fail("Integers can not handle exponents like this.");
|
||||
} catch (NumberFormatException expected) { }
|
||||
}
|
||||
|
||||
public void testValidJsonOnToString() throws Exception {
|
||||
JsonPrimitive json = new JsonPrimitive("Some\nEscaped\nValue");
|
||||
assertEquals("\"Some\\nEscaped\\nValue\"", json.toString());
|
||||
|
||||
json = new JsonPrimitive(new BigDecimal("1.333"));
|
||||
assertEquals("1.333", json.toString());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user