Added a new API method nullSafe() in TypeAdapter that can be used to avoid boilerplate handling of nulls in a type adapter.

This commit is contained in:
Inderjeet Singh 2011-12-05 19:50:49 +00:00
parent 538b7ca172
commit 91be944022
2 changed files with 107 additions and 11 deletions

View File

@ -16,17 +16,19 @@
package com.google.gson; package com.google.gson;
import com.google.gson.internal.bind.JsonElementWriter;
import com.google.gson.internal.bind.JsonTreeReader;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import java.io.StringWriter; import java.io.StringWriter;
import java.io.Writer; import java.io.Writer;
import com.google.gson.internal.bind.JsonElementWriter;
import com.google.gson.internal.bind.JsonTreeReader;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
/** /**
* Converts between Java objects and JSON. Applications use type adapters both * Converts between Java objects and JSON. Applications use type adapters both
* for customizing types' JSON forms, and for JSON conversions. * for customizing types' JSON forms, and for JSON conversions.
@ -135,6 +137,63 @@ public abstract class TypeAdapter<T> {
write(writer, value); write(writer, value);
} }
/**
* This wrapper method is used to make a type adapter null tolerant. In general, a
* type adapter is required to handle nulls in write and read methods. Here is how this
* is typically done:<br>
* <pre>{@code
Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
new TypeAdapter<Foo>() {
public Foo read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
// read a Foo from in and return it
}
public void write(JsonWriter out, Foo src) throws IOException {
if (src == null) {
out.nullValue();
return;
}
// write src as JSON to out
}
).create();
* }</pre>
* You can avoid this boilerplate handling of nulls by wrapping your type adapter with
* {@link #nullSafe(TypeAdapter)} method. Here is how we will rewrite the above example:
* <pre>{@code
Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
TypeAdapter.nullSafe(new TypeAdapter<Foo>() {
public Foo read(JsonReader in) throws IOException {
// read a Foo from in and return it
}
public void write(JsonWriter out, Foo src) throws IOException {
// write src as JSON to out
}
)).create();
* }</pre>
* Note that we didn't need to check for nulls in our type adapter after we used nullSafe.
*/
public static <T> TypeAdapter<T> nullSafe(final TypeAdapter<T> typeAdapter) {
return new TypeAdapter<T>() {
@Override public void write(JsonWriter out, T value) throws IOException {
if (value == null) {
out.nullValue();
} else {
typeAdapter.write(out, value);
}
}
@Override public T read(JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
return typeAdapter.read(reader);
}
};
}
/** /**
* Converts {@code value} to a JSON document. Unlike Gson's similar {@link * Converts {@code value} to a JSON document. Unlike Gson's similar {@link
* Gson#toJson(Object) toJson} method, this write is strict. Create a {@link * Gson#toJson(Object) toJson} method, this write is strict. Create a {@link

View File

@ -16,20 +16,24 @@
package com.google.gson.functional; package com.google.gson.functional;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import junit.framework.TestCase; import junit.framework.TestCase;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
public final class StreamingTypeAdaptersTest extends TestCase { public final class StreamingTypeAdaptersTest extends TestCase {
private Gson miniGson = new GsonBuilder().create(); private Gson miniGson = new GsonBuilder().create();
private TypeAdapter<Truck> truckAdapter = miniGson.getAdapter(Truck.class); private TypeAdapter<Truck> truckAdapter = miniGson.getAdapter(Truck.class);
@ -144,6 +148,39 @@ public final class StreamingTypeAdaptersTest extends TestCase {
assertTrue(Arrays.toString(array), Arrays.deepEquals(expected, array)); assertTrue(Arrays.toString(array), Arrays.deepEquals(expected, array));
} }
public void testNullSafe() {
TypeAdapter<Person> typeAdapter = new TypeAdapter<Person>() {
@Override public Person read(JsonReader in) throws IOException {
String[] values = in.nextString().split(",");
return new Person(values[0], Integer.parseInt(values[1]));
}
public void write(JsonWriter out, Person person) throws IOException {
out.value(person.name + "," + person.age);
}
};
Gson gson = new GsonBuilder().registerTypeAdapter(
Person.class, typeAdapter).create();
Truck truck = new Truck();
truck.horsePower = 1.0D;
truck.passengers = new ArrayList<Person>();
truck.passengers.add(null);
try {
gson.toJson(truck, Truck.class);
fail();
} catch (NullPointerException expected) {}
String json = "{horsePower:1.0,passengers:[null,null]}";
try {
gson.fromJson(json, Truck.class);
fail();
} catch (JsonSyntaxException expected) {}
gson = new GsonBuilder().registerTypeAdapter(
Person.class, TypeAdapter.nullSafe(typeAdapter)).create();
assertEquals("{\"horsePower\":1.0,\"passengers\":[null]}", gson.toJson(truck, Truck.class));
truck = gson.fromJson(json, Truck.class);
assertEquals(1.0D, truck.horsePower);
assertNull(truck.passengers.get(0));
}
public void testSerializeRecursive() throws IOException { public void testSerializeRecursive() throws IOException {
TypeAdapter<Node> nodeAdapter = miniGson.getAdapter(Node.class); TypeAdapter<Node> nodeAdapter = miniGson.getAdapter(Node.class);
Node root = new Node("root"); Node root = new Node("root");