From 65df3b97ba7352fd1d6afc925b9c30f5067a3f53 Mon Sep 17 00:00:00 2001 From: Jesse Wilson Date: Sun, 2 Oct 2011 16:59:56 +0000 Subject: [PATCH] Add a type adapter for JsonElement, so it serializes just like everything else. Fixes issue 362. --- gson/src/main/java/com/google/gson/Gson.java | 1 + .../com/google/gson/internal/Streams.java | 81 ++----------------- .../gson/internal/bind/TypeAdapters.java | 81 +++++++++++++++++++ .../functional/DefaultTypeAdaptersTest.java | 64 +++++++++++++++ 4 files changed, 151 insertions(+), 76 deletions(-) diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index 57171d55..7aa38b34 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -248,6 +248,7 @@ public final class Gson { .typeAdapter(BigDecimal.class, new BigDecimalTypeAdapter()) .typeAdapter(BigInteger.class, new BigIntegerTypeAdapter()) .factory(new CollectionTypeAdapterFactory(constructorConstructor)) + .factory(TypeAdapters.JSON_ELEMENT_FACTORY) .factory(ObjectTypeAdapter.FACTORY); for (TypeAdapter.Factory factory : typeAdapterFactories) { diff --git a/gson/src/main/java/com/google/gson/internal/Streams.java b/gson/src/main/java/com/google/gson/internal/Streams.java index 731a5027..3ac25907 100644 --- a/gson/src/main/java/com/google/gson/internal/Streams.java +++ b/gson/src/main/java/com/google/gson/internal/Streams.java @@ -16,27 +16,23 @@ package com.google.gson.internal; -import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonIOException; import com.google.gson.JsonNull; -import com.google.gson.JsonObject; import com.google.gson.JsonParseException; -import com.google.gson.JsonPrimitive; import com.google.gson.JsonSyntaxException; +import com.google.gson.internal.bind.TypeAdapters; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import com.google.gson.stream.MalformedJsonException; import java.io.EOFException; import java.io.IOException; import java.io.Writer; -import java.util.Map; /** * Reads and writes GSON parse trees over streams. */ public final class Streams { - /** * Takes a reader in any state and returns the next value as a JsonElement. */ @@ -45,7 +41,7 @@ public final class Streams { try { reader.peek(); isEmpty = false; - return parseRecursive(reader); + return TypeAdapters.JSON_ELEMENT.read(reader); } catch (EOFException e) { /* * For compatibility with JSON 1.5 and earlier, we return a JsonNull for @@ -64,79 +60,11 @@ public final class Streams { } } - private static JsonElement parseRecursive(JsonReader reader) throws IOException { - switch (reader.peek()) { - case STRING: - return new JsonPrimitive(reader.nextString()); - case NUMBER: - String number = reader.nextString(); - return new JsonPrimitive(new LazilyParsedNumber(number)); - case BOOLEAN: - return new JsonPrimitive(reader.nextBoolean()); - case NULL: - reader.nextNull(); - return JsonNull.INSTANCE; - case BEGIN_ARRAY: - JsonArray array = new JsonArray(); - reader.beginArray(); - while (reader.hasNext()) { - array.add(parseRecursive(reader)); - } - reader.endArray(); - return array; - case BEGIN_OBJECT: - JsonObject object = new JsonObject(); - reader.beginObject(); - while (reader.hasNext()) { - object.add(reader.nextName(), parseRecursive(reader)); - } - reader.endObject(); - return object; - case END_DOCUMENT: - case NAME: - case END_OBJECT: - case END_ARRAY: - default: - throw new IllegalArgumentException(); - } - } - /** * Writes the JSON element to the writer, recursively. */ - public static void write(JsonElement element, JsonWriter writer) - throws IOException { - if (element == null || element.isJsonNull()) { - writer.nullValue(); - } else if (element.isJsonPrimitive()) { - JsonPrimitive primitive = element.getAsJsonPrimitive(); - if (primitive.isNumber()) { - writer.value(primitive.getAsNumber()); - } else if (primitive.isBoolean()) { - writer.value(primitive.getAsBoolean()); - } else { - writer.value(primitive.getAsString()); - } - - } else if (element.isJsonArray()) { - writer.beginArray(); - for (JsonElement e : element.getAsJsonArray()) { - write(e, writer); - } - writer.endArray(); - - } else if (element.isJsonObject()) { - writer.beginObject(); - for (Map.Entry e : element.getAsJsonObject().entrySet()) { - JsonElement value = e.getValue(); - writer.name(e.getKey()); - write(value, writer); - } - writer.endObject(); - - } else { - throw new IllegalArgumentException("Couldn't write " + element.getClass()); - } + public static void write(JsonElement element, JsonWriter writer) throws IOException { + TypeAdapters.JSON_ELEMENT.write(writer, element); } public static Writer writerForAppendable(Appendable appendable) { @@ -183,4 +111,5 @@ public final class Streams { } } } + } diff --git a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java index 07d764a9..51d49793 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java +++ b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java @@ -16,7 +16,12 @@ package com.google.gson.internal.bind; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonIOException; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; import com.google.gson.JsonSyntaxException; import com.google.gson.internal.LazilyParsedNumber; import com.google.gson.reflect.TypeToken; @@ -34,6 +39,7 @@ import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; +import java.util.Map; import java.util.StringTokenizer; import java.util.UUID; @@ -526,6 +532,81 @@ public final class TypeAdapters { public static final TypeAdapter.Factory LOCALE_FACTORY = newFactory(Locale.class, LOCALE); + public static final TypeAdapter JSON_ELEMENT = new TypeAdapter() { + @Override public JsonElement read(JsonReader reader) throws IOException { + switch (reader.peek()) { + case STRING: + return new JsonPrimitive(reader.nextString()); + case NUMBER: + String number = reader.nextString(); + return new JsonPrimitive(new LazilyParsedNumber(number)); + case BOOLEAN: + return new JsonPrimitive(reader.nextBoolean()); + case NULL: + reader.nextNull(); + return JsonNull.INSTANCE; + case BEGIN_ARRAY: + JsonArray array = new JsonArray(); + reader.beginArray(); + while (reader.hasNext()) { + array.add(read(reader)); + } + reader.endArray(); + return array; + case BEGIN_OBJECT: + JsonObject object = new JsonObject(); + reader.beginObject(); + while (reader.hasNext()) { + object.add(reader.nextName(), read(reader)); + } + reader.endObject(); + return object; + case END_DOCUMENT: + case NAME: + case END_OBJECT: + case END_ARRAY: + default: + throw new IllegalArgumentException(); + } + } + + @Override public void write(JsonWriter writer, JsonElement value) throws IOException { + if (value == null || value.isJsonNull()) { + writer.nullValue(); + } else if (value.isJsonPrimitive()) { + JsonPrimitive primitive = value.getAsJsonPrimitive(); + if (primitive.isNumber()) { + writer.value(primitive.getAsNumber()); + } else if (primitive.isBoolean()) { + writer.value(primitive.getAsBoolean()); + } else { + writer.value(primitive.getAsString()); + } + + } else if (value.isJsonArray()) { + writer.beginArray(); + for (JsonElement e : value.getAsJsonArray()) { + write(writer, e); + } + writer.endArray(); + + } else if (value.isJsonObject()) { + writer.beginObject(); + for (Map.Entry e : value.getAsJsonObject().entrySet()) { + writer.name(e.getKey()); + write(writer, e.getValue()); + } + writer.endObject(); + + } else { + throw new IllegalArgumentException("Couldn't write " + value.getClass()); + } + } + }; + + public static final TypeAdapter.Factory JSON_ELEMENT_FACTORY + = newFactory(JsonElement.class, JSON_ELEMENT); + private static final class EnumTypeAdapter> extends TypeAdapter { private final Class classOfT; diff --git a/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java b/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java index 79c74246..75e94ddc 100644 --- a/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java +++ b/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java @@ -17,10 +17,14 @@ package com.google.gson.functional; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; import java.math.BigDecimal; @@ -459,6 +463,66 @@ public class DefaultTypeAdaptersTest extends TestCase { } } + public void testJsonPrimitiveSerialization() { + assertEquals("5", gson.toJson(new JsonPrimitive(5), JsonElement.class)); + assertEquals("true", gson.toJson(new JsonPrimitive(true), JsonElement.class)); + assertEquals("\"foo\"", gson.toJson(new JsonPrimitive("foo"), JsonElement.class)); + assertEquals("\"a\"", gson.toJson(new JsonPrimitive('a'), JsonElement.class)); + } + + public void testJsonPrimitiveDeserialization() { + assertEquals(new JsonPrimitive(5), gson.fromJson("5", JsonElement.class)); + assertEquals(new JsonPrimitive(true), gson.fromJson("true", JsonElement.class)); + assertEquals(new JsonPrimitive("foo"), gson.fromJson("\"foo\"", JsonElement.class)); + assertEquals(new JsonPrimitive('a'), gson.fromJson("\"a\"", JsonElement.class)); + } + + public void testJsonNullSerialization() { + assertEquals("null", gson.toJson(JsonNull.INSTANCE, JsonElement.class)); + } + + public void testNullJsonElementSerialization() { + assertEquals("null", gson.toJson(null, JsonElement.class)); + } + + public void testJsonArraySerialization() { + JsonArray array = new JsonArray(); + array.add(new JsonPrimitive(1)); + array.add(new JsonPrimitive(2)); + array.add(new JsonPrimitive(3)); + assertEquals("[1,2,3]", gson.toJson(array, JsonElement.class)); + } + + public void testJsonArrayDeerialization() { + JsonArray array = new JsonArray(); + array.add(new JsonPrimitive(1)); + array.add(new JsonPrimitive(2)); + array.add(new JsonPrimitive(3)); + assertEquals(array, gson.fromJson("[1,2,3]", JsonElement.class)); + } + + public void testJsonObjectSerialization() { + JsonObject object = new JsonObject(); + object.add("foo", new JsonPrimitive(1)); + object.add("bar", new JsonPrimitive(2)); + assertEquals("{\"foo\":1,\"bar\":2}", gson.toJson(object, JsonElement.class)); + } + + public void testJsonObjectDeerialization() { + JsonObject object = new JsonObject(); + object.add("foo", new JsonPrimitive(1)); + object.add("bar", new JsonPrimitive(2)); + assertEquals(object, gson.fromJson("{\"foo\":1,\"bar\":2}", JsonElement.class)); + } + + public void testJsonNullDeerialization() { + assertEquals(JsonNull.INSTANCE, gson.fromJson("null", JsonElement.class)); + } + + public void testNullJsonElementDeserialization() { + assertEquals(JsonNull.INSTANCE, gson.fromJson("null", JsonElement.class)); + } + private static class ClassWithBigDecimal { BigDecimal value; ClassWithBigDecimal(String value) {