From 50eb582657b5a07aa4aec22f11c9f3071eb631a8 Mon Sep 17 00:00:00 2001 From: Joel Leitch Date: Wed, 23 Sep 2009 18:28:03 +0000 Subject: [PATCH] Ensure that the JsonElement.toString() will always generate valid JSON (even when control characters/whitespace characters are used). --- gson/src/main/java/com/google/gson/Gson.java | 1 - .../main/java/com/google/gson/JsonArray.java | 4 +- .../com/google/gson/JsonCompactFormatter.java | 14 +- .../java/com/google/gson/JsonElement.java | 5 +- .../com/google/gson/JsonEscapingVisitor.java | 68 ------- .../main/java/com/google/gson/JsonNull.java | 2 +- .../main/java/com/google/gson/JsonObject.java | 4 +- .../java/com/google/gson/JsonPrimitive.java | 8 +- .../com/google/gson/JsonPrintFormatter.java | 20 +- .../google/gson/JsonEscapingVisitorTest.java | 182 ------------------ .../com/google/gson/JsonPrimitiveTest.java | 8 + 11 files changed, 41 insertions(+), 275 deletions(-) delete mode 100644 gson/src/main/java/com/google/gson/JsonEscapingVisitor.java delete mode 100644 gson/src/test/java/com/google/gson/JsonEscapingVisitorTest.java diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index 1bf41b5b..06533d98 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -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; diff --git a/gson/src/main/java/com/google/gson/JsonArray.java b/gson/src/main/java/com/google/gson/JsonArray.java index 892c7fd7..2cac2992 100644 --- a/gson/src/main/java/com/google/gson/JsonArray.java +++ b/gson/src/main/java/com/google/gson/JsonArray.java @@ -293,7 +293,7 @@ public final class JsonArray extends JsonElement implements Iterable 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('}'); } diff --git a/gson/src/main/java/com/google/gson/JsonPrimitive.java b/gson/src/main/java/com/google/gson/JsonPrimitive.java index b053c66d..fedf64d2 100644 --- a/gson/src/main/java/com/google/gson/JsonPrimitive.java +++ b/gson/src/main/java/com/google/gson/JsonPrimitive.java @@ -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()); diff --git a/gson/src/main/java/com/google/gson/JsonPrintFormatter.java b/gson/src/main/java/com/google/gson/JsonPrintFormatter.java index 76cb32a3..5a6413f3 100644 --- a/gson/src/main/java/com/google/gson/JsonPrintFormatter.java +++ b/gson/src/main/java/com/google/gson/JsonPrintFormatter.java @@ -146,11 +146,13 @@ final class JsonPrintFormatter implements JsonFormatter { private final Map firstArrayElement; private final Map 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(); this.firstObjectMember = new HashMap(); @@ -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(); diff --git a/gson/src/test/java/com/google/gson/JsonEscapingVisitorTest.java b/gson/src/test/java/com/google/gson/JsonEscapingVisitorTest.java deleted file mode 100644 index 409cf335..00000000 --- a/gson/src/test/java/com/google/gson/JsonEscapingVisitorTest.java +++ /dev/null @@ -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 - } - } -} diff --git a/gson/src/test/java/com/google/gson/JsonPrimitiveTest.java b/gson/src/test/java/com/google/gson/JsonPrimitiveTest.java index 0d2eb8cb..7561daa6 100644 --- a/gson/src/test/java/com/google/gson/JsonPrimitiveTest.java +++ b/gson/src/test/java/com/google/gson/JsonPrimitiveTest.java @@ -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()); + } }