Support mixed streaming and databinding with new APIs.
This commit is contained in:
parent
ffdf0e7012
commit
747e3c3051
@ -17,6 +17,7 @@
|
||||
package com.google.gson;
|
||||
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
@ -110,6 +111,7 @@ public final class Gson {
|
||||
|
||||
private final JsonFormatter formatter;
|
||||
private final boolean serializeNulls;
|
||||
private final boolean htmlSafe;
|
||||
|
||||
private final boolean generateNonExecutableJson;
|
||||
|
||||
@ -151,7 +153,7 @@ public final class Gson {
|
||||
this(DEFAULT_EXCLUSION_STRATEGY, DEFAULT_EXCLUSION_STRATEGY, DEFAULT_NAMING_POLICY,
|
||||
new MappedObjectConstructor(DefaultTypeAdapters.getDefaultInstanceCreators()),
|
||||
DEFAULT_JSON_FORMATTER, false, DefaultTypeAdapters.getDefaultSerializers(),
|
||||
DefaultTypeAdapters.getDefaultDeserializers(), DEFAULT_JSON_NON_EXECUTABLE);
|
||||
DefaultTypeAdapters.getDefaultDeserializers(), DEFAULT_JSON_NON_EXECUTABLE, true);
|
||||
}
|
||||
|
||||
Gson(ExclusionStrategy serializationStrategy, ExclusionStrategy deserializationStrategy,
|
||||
@ -159,7 +161,7 @@ public final class Gson {
|
||||
JsonFormatter formatter, boolean serializeNulls,
|
||||
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers,
|
||||
ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers,
|
||||
boolean generateNonExecutableGson) {
|
||||
boolean generateNonExecutableGson, boolean htmlSafe) {
|
||||
this.serializationStrategy = serializationStrategy;
|
||||
this.deserializationStrategy = deserializationStrategy;
|
||||
this.fieldNamingPolicy = fieldNamingPolicy;
|
||||
@ -169,6 +171,7 @@ public final class Gson {
|
||||
this.serializers = serializers;
|
||||
this.deserializers = deserializers;
|
||||
this.generateNonExecutableJson = generateNonExecutableGson;
|
||||
this.htmlSafe = htmlSafe;
|
||||
}
|
||||
|
||||
private ObjectNavigatorFactory createDefaultObjectNavigatorFactory(ExclusionStrategy strategy) {
|
||||
@ -268,7 +271,7 @@ public final class Gson {
|
||||
*/
|
||||
public String toJson(Object src, Type typeOfSrc) {
|
||||
StringWriter writer = new StringWriter();
|
||||
toJson(src, typeOfSrc, writer);
|
||||
toJson(toJsonTree(src, typeOfSrc), writer);
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
@ -317,6 +320,14 @@ public final class Gson {
|
||||
toJson(jsonElement, writer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the JSON representation of {@code src} of type {@code typeOfSrc} to
|
||||
* {@code writer}.
|
||||
*/
|
||||
public void toJson(Object src, Type typeOfSrc, JsonWriter writer) {
|
||||
toJson(toJsonTree(src, typeOfSrc), writer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a tree of {@link JsonElement}s into its equivalent JSON representation.
|
||||
*
|
||||
@ -342,15 +353,30 @@ public final class Gson {
|
||||
if (generateNonExecutableJson) {
|
||||
writer.append(JSON_NON_EXECUTABLE_PREFIX);
|
||||
}
|
||||
if (jsonElement == null && serializeNulls) {
|
||||
writeOutNullString(writer);
|
||||
}
|
||||
formatter.format(jsonElement, writer, serializeNulls);
|
||||
toJson(jsonElement, new JsonWriter(Streams.writerForAppendable(writer)));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the JSON for {@code jsonElement} to {@code writer}.
|
||||
*/
|
||||
public void toJson(JsonElement jsonElement, JsonWriter writer) {
|
||||
boolean oldLenient = writer.isLenient();
|
||||
writer.setLenient(true);
|
||||
boolean oldHtmlSafe = writer.isHtmlSafe();
|
||||
writer.setHtmlSafe(htmlSafe);
|
||||
try {
|
||||
Streams.write(jsonElement, serializeNulls, writer);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
writer.setLenient(oldLenient);
|
||||
writer.setHtmlSafe(oldHtmlSafe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method deserializes the specified Json into an object of the specified class. It is not
|
||||
* suitable to use if the specified class is a generic type since it will not have the generic
|
||||
@ -417,7 +443,7 @@ public final class Gson {
|
||||
* @since 1.2
|
||||
*/
|
||||
public <T> T fromJson(Reader json, Class<T> classOfT) throws JsonParseException {
|
||||
T target = classOfT.cast(fromJson(json, (Type) classOfT));
|
||||
T target = classOfT.cast(fromJson(new JsonReader(json), classOfT));
|
||||
return target;
|
||||
}
|
||||
|
||||
@ -439,12 +465,24 @@ public final class Gson {
|
||||
* @throws JsonParseException if json is not a valid representation for an object of type typeOfT
|
||||
* @since 1.2
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T fromJson(Reader json, Type typeOfT) throws JsonParseException {
|
||||
JsonReader jsonReader = new JsonReader(json);
|
||||
jsonReader.setLenient(true);
|
||||
JsonElement root = Streams.parse(jsonReader);
|
||||
return (T) fromJson(root, typeOfT);
|
||||
return this.<T>fromJson(new JsonReader(json), typeOfT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next JSON value from {@code reader} and convert it to an object
|
||||
* of type {@code typeOfT}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked") // this method is unsafe and should be used very carefully
|
||||
public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonParseException {
|
||||
boolean oldLenient = reader.isLenient();
|
||||
reader.setLenient(true);
|
||||
try {
|
||||
JsonElement root = Streams.parse(reader);
|
||||
return (T) fromJson(root, typeOfT);
|
||||
} finally {
|
||||
reader.setLenient(oldLenient);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -546,7 +546,7 @@ public final class GsonBuilder {
|
||||
new JsonPrintFormatter(escapeHtmlChars) : new JsonCompactFormatter(escapeHtmlChars);
|
||||
Gson gson = new Gson(serializationExclusionStrategy, deserializationExclusionStrategy,
|
||||
fieldNamingPolicy, objConstructor, formatter, serializeNulls, customSerializers,
|
||||
customDeserializers, generateNonExecutableJson);
|
||||
customDeserializers, generateNonExecutableJson, escapeHtmlChars);
|
||||
return gson;
|
||||
}
|
||||
|
||||
|
@ -50,10 +50,20 @@ public final class JsonParser {
|
||||
* @since 1.3
|
||||
*/
|
||||
public JsonElement parse(Reader json) throws JsonParseException {
|
||||
return parse(new JsonReader(json));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next value from the JSON stream as a parse tree.
|
||||
*
|
||||
* @throws JsonParseException if there is an IOException or if the specified
|
||||
* text is not valid JSON
|
||||
*/
|
||||
public JsonElement parse(JsonReader json) throws JsonParseException {
|
||||
boolean lenient = json.isLenient();
|
||||
json.setLenient(true);
|
||||
try {
|
||||
JsonReader jsonReader = new JsonReader(json);
|
||||
jsonReader.setLenient(true);
|
||||
return Streams.parse(jsonReader);
|
||||
return Streams.parse(json);
|
||||
} catch (StackOverflowError e) {
|
||||
throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e);
|
||||
} catch (OutOfMemoryError e) {
|
||||
@ -64,6 +74,8 @@ public final class JsonParser {
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
} finally {
|
||||
json.setLenient(lenient);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,6 +142,8 @@ public final class JsonWriter implements Closeable {
|
||||
|
||||
private boolean lenient;
|
||||
|
||||
private boolean htmlSafe;
|
||||
|
||||
/**
|
||||
* Creates a new instance that writes a JSON-encoded stream to {@code out}.
|
||||
* For best performance, ensure {@link Writer} is buffered; wrapping in
|
||||
@ -188,6 +190,32 @@ public final class JsonWriter implements Closeable {
|
||||
this.lenient = lenient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this writer has relaxed syntax rules.
|
||||
*/
|
||||
public boolean isLenient() {
|
||||
return lenient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure this writer to emit JSON that's safe for direct inclusion in HTML
|
||||
* and XML documents. This escapes the HTML characters {@code <}, {@code >},
|
||||
* {@code &} and {@code =} before writing them to the stream. Without this
|
||||
* setting, your XML/HTML encoder should replace these characters with the
|
||||
* corresponding escape sequences.
|
||||
*/
|
||||
public void setHtmlSafe(boolean htmlSafe) {
|
||||
this.htmlSafe = htmlSafe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this writer writes JSON that's safe for inclusion in HTML
|
||||
* and XML documents.
|
||||
*/
|
||||
public boolean isHtmlSafe() {
|
||||
return htmlSafe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Begins encoding a new array. Each call to this method must be paired with
|
||||
* a call to {@link #endArray}.
|
||||
@ -430,6 +458,18 @@ public final class JsonWriter implements Closeable {
|
||||
out.write("\\f");
|
||||
break;
|
||||
|
||||
case '<':
|
||||
case '>':
|
||||
case '&':
|
||||
case '=':
|
||||
case '\'':
|
||||
if (htmlSafe) {
|
||||
out.write(String.format("\\u%04x", (int) c));
|
||||
} else {
|
||||
out.write(c);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (c <= 0x1F) {
|
||||
out.write(String.format("\\u%04x", (int) c));
|
||||
|
@ -42,7 +42,7 @@ public class FunctionWithInternalDependenciesTest extends TestCase {
|
||||
Gson gson = new Gson(exclusionStrategy, exclusionStrategy, Gson.DEFAULT_NAMING_POLICY,
|
||||
new MappedObjectConstructor(DefaultTypeAdapters.getDefaultInstanceCreators()),
|
||||
Gson.DEFAULT_JSON_FORMATTER, false, DefaultTypeAdapters.getDefaultSerializers(),
|
||||
DefaultTypeAdapters.getDefaultDeserializers(), Gson.DEFAULT_JSON_NON_EXECUTABLE);
|
||||
DefaultTypeAdapters.getDefaultDeserializers(), Gson.DEFAULT_JSON_NON_EXECUTABLE, true);
|
||||
assertEquals("{}", gson.toJson(new ClassWithNoFields() {
|
||||
// empty anonymous class
|
||||
}));
|
||||
|
94
gson/src/test/java/com/google/gson/MixedStreamTest.java
Normal file
94
gson/src/test/java/com/google/gson/MixedStreamTest.java
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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 com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public final class MixedStreamTest extends TestCase {
|
||||
|
||||
private static final Car BLUE_MUSTANG = new Car("mustang", 0x0000FF);
|
||||
private static final Car BLACK_BMW = new Car("bmw", 0x000000);
|
||||
private static final Car RED_MIATA = new Car("miata", 0xFF0000);
|
||||
private static final String CARS_JSON = "[\n"
|
||||
+ " {\n"
|
||||
+ " \"name\": \"mustang\",\n"
|
||||
+ " \"color\": 255\n"
|
||||
+ " },\n"
|
||||
+ " {\n"
|
||||
+ " \"name\": \"bmw\",\n"
|
||||
+ " \"color\": 0\n"
|
||||
+ " },\n"
|
||||
+ " {\n"
|
||||
+ " \"name\": \"miata\",\n"
|
||||
+ " \"color\": 16711680\n"
|
||||
+ " }\n"
|
||||
+ "]";
|
||||
|
||||
public void testWriteMixedStreamed() throws IOException {
|
||||
Gson gson = new Gson();
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||
|
||||
jsonWriter.beginArray();
|
||||
jsonWriter.setIndent(" ");
|
||||
gson.toJson(BLUE_MUSTANG, Car.class, jsonWriter);
|
||||
gson.toJson(BLACK_BMW, Car.class, jsonWriter);
|
||||
gson.toJson(RED_MIATA, Car.class, jsonWriter);
|
||||
jsonWriter.endArray();
|
||||
|
||||
assertEquals(CARS_JSON, stringWriter.toString());
|
||||
}
|
||||
|
||||
public void testReadMixedStreamed() throws IOException {
|
||||
Gson gson = new Gson();
|
||||
StringReader stringReader = new StringReader(CARS_JSON);
|
||||
JsonReader jsonReader = new JsonReader(stringReader);
|
||||
|
||||
jsonReader.beginArray();
|
||||
assertEquals(BLUE_MUSTANG, gson.fromJson(jsonReader, Car.class));
|
||||
assertEquals(BLACK_BMW, gson.fromJson(jsonReader, Car.class));
|
||||
assertEquals(RED_MIATA, gson.fromJson(jsonReader, Car.class));
|
||||
jsonReader.endArray();
|
||||
}
|
||||
|
||||
static final class Car {
|
||||
String name;
|
||||
int color;
|
||||
|
||||
Car(String name, int color) {
|
||||
this.name = name;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
Car() {} // for Gson
|
||||
|
||||
@Override public int hashCode() {
|
||||
return name.hashCode() ^ color;
|
||||
}
|
||||
|
||||
@Override public boolean equals(Object o) {
|
||||
return o instanceof Car
|
||||
&& ((Car) o).name.equals(name)
|
||||
&& ((Car) o).color == color;
|
||||
}
|
||||
}
|
||||
}
|
@ -54,7 +54,7 @@ public class EscapingTest extends TestCase {
|
||||
strings.add("&");
|
||||
strings.add("'");
|
||||
strings.add("\"");
|
||||
assertEquals("[\"\\u003c\",\"\\u003e\",\"\\u003d\",\"\\u0026\",\"'\",\"\\\"\"]",
|
||||
assertEquals("[\"\\u003c\",\"\\u003e\",\"\\u003d\",\"\\u0026\",\"\\u0027\",\"\\\"\"]",
|
||||
gson.toJson(strings));
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ public class NullObjectAndFieldTest extends TestCase {
|
||||
gsonBuilder = new GsonBuilder();
|
||||
Gson gson = gsonBuilder.setPrettyPrinting().create();
|
||||
String result = gson.toJson(new ClassWithMembers());
|
||||
assertEquals("{}\n", result);
|
||||
assertEquals("{}", result);
|
||||
|
||||
gson = gsonBuilder.serializeNulls().create();
|
||||
result = gson.toJson(new ClassWithMembers());
|
||||
@ -142,11 +142,11 @@ public class NullObjectAndFieldTest extends TestCase {
|
||||
gsonBuilder = new GsonBuilder();
|
||||
Gson gson = gsonBuilder.setPrettyPrinting().create();
|
||||
String result = gson.toJson(new String[] { "1", null, "3" });
|
||||
assertEquals("[\"1\",null,\"3\"]\n", result);
|
||||
assertEquals("[\"1\",null,\"3\"]", result);
|
||||
|
||||
gson = gsonBuilder.serializeNulls().create();
|
||||
result = gson.toJson(new String[] { "1", null, "3" });
|
||||
assertEquals("[\"1\",null,\"3\"]\n", result);
|
||||
assertEquals("[\"1\",null,\"3\"]", result);
|
||||
}
|
||||
|
||||
private static class ClassWithNullWrappedPrimitive {
|
||||
|
@ -59,34 +59,32 @@ public class PrettyPrintingTest extends TestCase {
|
||||
Type typeOfSrc = new TypeToken<List<BagOfPrimitives>>() {}.getType();
|
||||
String json = gson.toJson(listOfB, typeOfSrc);
|
||||
print(json);
|
||||
assertPrintMargin(json);
|
||||
}
|
||||
|
||||
public void testPrettyPrintArrayOfObjects() {
|
||||
ArrayOfObjects target = new ArrayOfObjects();
|
||||
String json = gson.toJson(target);
|
||||
print(json);
|
||||
assertPrintMargin(json);
|
||||
}
|
||||
|
||||
public void testPrettyPrintArrayOfPrimitives() {
|
||||
int[] ints = new int[] { 1, 2, 3, 4, 5 };
|
||||
String json = gson.toJson(ints);
|
||||
assertEquals("[1,2,3,4,5]\n", json);
|
||||
assertEquals("[1,2,3,4,5]", json);
|
||||
}
|
||||
|
||||
public void testPrettyPrintArrayOfPrimitiveArrays() {
|
||||
int[][] ints = new int[][] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 },
|
||||
{ 9, 0 }, { 10 } };
|
||||
String json = gson.toJson(ints);
|
||||
assertEquals("[[1,2],[3,4],[5,6],[7,8],[9,0],[10]]\n", json);
|
||||
assertEquals("[[1,2],[3,4],[5,6],[7,8],[9,0],[10]]", json);
|
||||
}
|
||||
|
||||
public void testPrettyPrintListOfPrimitiveArrays() {
|
||||
List<Integer[]> list = Arrays.asList(new Integer[][] { { 1, 2 }, { 3, 4 },
|
||||
{ 5, 6 }, { 7, 8 }, { 9, 0 }, { 10 } });
|
||||
String json = gson.toJson(list);
|
||||
assertEquals("[[1,2],[3,4],[5,6],[7,8],[9,0],[10]]\n", json);
|
||||
assertEquals("[[1,2],[3,4],[5,6],[7,8],[9,0],[10]]", json);
|
||||
}
|
||||
|
||||
public void testMap() {
|
||||
@ -94,7 +92,7 @@ public class PrettyPrintingTest extends TestCase {
|
||||
map.put("abc", 1);
|
||||
map.put("def", 5);
|
||||
String json = gson.toJson(map);
|
||||
assertEquals("{\"abc\":1,\"def\":5}\n", json);
|
||||
assertEquals("{\"abc\":1,\"def\":5}", json);
|
||||
}
|
||||
|
||||
// In response to bug 153
|
||||
@ -114,7 +112,7 @@ public class PrettyPrintingTest extends TestCase {
|
||||
public void testMultipleArrays() {
|
||||
int[][][] ints = new int[][][] { { { 1 }, { 2 } } };
|
||||
String json = gson.toJson(ints);
|
||||
assertEquals("[[[1],[2]]]\n", json);
|
||||
assertEquals("[[[1],[2]]]", json);
|
||||
}
|
||||
|
||||
private void print(String msg) {
|
||||
@ -122,16 +120,4 @@ public class PrettyPrintingTest extends TestCase {
|
||||
System.out.println(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertPrintMargin(String str) {
|
||||
int position = 0;
|
||||
char[] chars = str.toCharArray();
|
||||
for (int i = 0; i < chars.length; ++i, ++position) {
|
||||
char c = chars[i];
|
||||
if (c == '\n') {
|
||||
position = 0;
|
||||
}
|
||||
assertTrue(position <= PRINT_MARGIN - RIGHT_MARGIN + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user