Enable serialization of nulls within Maps.

This commit is contained in:
Joel Leitch 2008-11-30 23:01:14 +00:00
parent a82c400dc8
commit b90228dcc0
10 changed files with 93 additions and 23 deletions

View File

@ -31,7 +31,6 @@ import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Collection; import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -374,7 +373,7 @@ final class DefaultTypeAdapters {
public JsonElement serialize(Collection src, Type typeOfSrc, JsonSerializationContext context) { public JsonElement serialize(Collection src, Type typeOfSrc, JsonSerializationContext context) {
if (src == null) { if (src == null) {
return JsonNull.INSTANCE; return JsonNull.createJsonNull();
} }
JsonArray array = new JsonArray(); JsonArray array = new JsonArray();
Type childGenericType = null; Type childGenericType = null;
@ -432,13 +431,19 @@ final class DefaultTypeAdapters {
if (typeOfSrc instanceof ParameterizedType) { if (typeOfSrc instanceof ParameterizedType) {
childGenericType = new TypeInfoMap(typeOfSrc).getValueType(); childGenericType = new TypeInfoMap(typeOfSrc).getValueType();
} }
for (Iterator iterator = src.entrySet().iterator(); iterator.hasNext(); ) {
Map.Entry entry = (Map.Entry) iterator.next(); for (Map.Entry entry : (Set<Map.Entry>) src.entrySet()) {
Object value = entry.getValue(); Object value = entry.getValue();
Type childType = (childGenericType == null) ?
childType = value.getClass() : childGenericType; JsonElement valueElement;
JsonElement valueElement = context.serialize(value, childType); if (value == null) {
map.add(entry.getKey().toString(), valueElement); valueElement = JsonNull.createJsonNull();
} else {
Type childType = (childGenericType == null) ?
childType = value.getClass() : childGenericType;
valueElement = context.serialize(value, childType);
}
map.add(String.valueOf(entry.getKey()), valueElement);
} }
return map; return map;
} }

View File

@ -61,7 +61,7 @@ final class JsonArrayDeserializationVisitor<T> extends JsonDeserializationVisito
JsonElement jsonChild = jsonArray.get(i); JsonElement jsonChild = jsonArray.get(i);
Object child; Object child;
if (jsonChild == null) { if (jsonChild == null || jsonChild.isJsonNull()) {
child = null; child = null;
} else if (jsonChild instanceof JsonObject) { } else if (jsonChild instanceof JsonObject) {
child = visitChildAsObject(arrayTypeInfo.getComponentRawType(), jsonChild); child = visitChildAsObject(arrayTypeInfo.getComponentRawType(), jsonChild);

View File

@ -17,7 +17,7 @@
package com.google.gson; package com.google.gson;
/** /**
* A class representing a Json null value. * A class representing a Json {@code null} value.
* *
* @author Inderjeet Singh * @author Inderjeet Singh
* @author Joel Leitch * @author Joel Leitch
@ -25,7 +25,19 @@ package com.google.gson;
*/ */
public final class JsonNull extends JsonElement { public final class JsonNull extends JsonElement {
static final JsonNull INSTANCE = new JsonNull(); private static final JsonNull INSTANCE = new JsonNull(true);
/**
* @deprecated use the creation method, {@link #createJsonNull()}, instead.
*/
@Deprecated
public JsonNull() {
// Do nothing
}
private JsonNull(boolean placeholder) {
// Prevent instantiation
}
@Override @Override
protected void toString(StringBuilder sb) { protected void toString(StringBuilder sb) {
@ -46,5 +58,9 @@ public final class JsonNull extends JsonElement {
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
return other instanceof JsonNull; return other instanceof JsonNull;
} }
public static JsonNull createJsonNull() {
return INSTANCE;
}
} }

View File

@ -115,7 +115,7 @@ public final class JsonObject extends JsonElement {
public JsonElement get(String memberName) { public JsonElement get(String memberName) {
if (members.containsKey(memberName)) { if (members.containsKey(memberName)) {
JsonElement member = members.get(memberName); JsonElement member = members.get(memberName);
return member == null ? JsonNull.INSTANCE : member; return member == null ? JsonNull.createJsonNull() : member;
} else { } else {
return null; return null;
} }

View File

@ -93,7 +93,7 @@ final class JsonObjectDeserializationVisitor<T> extends JsonDeserializationVisit
JsonElement child = json.getAsJsonObject().get(fName); JsonElement child = json.getAsJsonObject().get(fName);
if (child == null) { if (child == null) {
return true; return true;
} else if (JsonNull.INSTANCE.equals(child)) { } else if (child.isJsonNull()) {
TypeInfo typeInfo = new TypeInfo(actualTypeOfField); TypeInfo typeInfo = new TypeInfo(actualTypeOfField);
if (!typeInfo.isPrimitive()) { if (!typeInfo.isPrimitive()) {
f.set(parent, null); f.set(parent, null);

View File

@ -53,7 +53,7 @@ final class JsonParser implements JsonParserConstants {
final private JsonNull JsonNull() throws ParseException { final private JsonNull JsonNull() throws ParseException {
Token t; Token t;
t = jj_consume_token(NULL); t = jj_consume_token(NULL);
{if (true) return JsonNull.INSTANCE;} {if (true) return JsonNull.createJsonNull();}
throw new Error("Missing return statement in function"); throw new Error("Missing return statement in function");
} }
@ -159,7 +159,7 @@ final class JsonParser implements JsonParserConstants {
o = JsonBoolean(); o = JsonBoolean();
break; break;
case NULL: case NULL:
jj_consume_token(NULL); o = JsonNull();
break; break;
default: default:
jj_la1[6] = jj_gen; jj_la1[6] = jj_gen;

View File

@ -67,7 +67,7 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
public void visitArrayField(Field f, Type typeOfF, Object obj) { public void visitArrayField(Field f, Type typeOfF, Object obj) {
if (isFieldNull(f, obj)) { if (isFieldNull(f, obj)) {
if (serializeNulls) { if (serializeNulls) {
addChildAsElement(f, JsonNull.INSTANCE); addChildAsElement(f, JsonNull.createJsonNull());
} }
} else { } else {
Object array = getFieldValue(f, obj); Object array = getFieldValue(f, obj);
@ -78,7 +78,7 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
public void visitObjectField(Field f, Type typeOfF, Object obj) { public void visitObjectField(Field f, Type typeOfF, Object obj) {
if (isFieldNull(f, obj)) { if (isFieldNull(f, obj)) {
if (serializeNulls) { if (serializeNulls) {
addChildAsElement(f, JsonNull.INSTANCE); addChildAsElement(f, JsonNull.createJsonNull());
} }
} else { } else {
Object fieldValue = getFieldValue(f, obj); Object fieldValue = getFieldValue(f, obj);
@ -127,7 +127,7 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
JsonSerializer serializer = serializers.getHandlerFor(objType); JsonSerializer serializer = serializers.getHandlerFor(objType);
if (serializer != null) { if (serializer != null) {
if (obj == null) { if (obj == null) {
assignToRoot(JsonNull.INSTANCE); assignToRoot(JsonNull.createJsonNull());
} else { } else {
assignToRoot(serializer.serialize(obj, objType, context)); assignToRoot(serializer.serialize(obj, objType, context));
} }
@ -143,7 +143,7 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
Object obj = f.get(parent); Object obj = f.get(parent);
if (obj == null) { if (obj == null) {
if (serializeNulls) { if (serializeNulls) {
addChildAsElement(f, JsonNull.INSTANCE); addChildAsElement(f, JsonNull.createJsonNull());
} }
return true; return true;
} }

View File

@ -47,7 +47,7 @@ final class JsonTreeNavigator {
} }
} }
visitor.endArray(array); visitor.endArray(array);
} else if (element.isJsonObject()){ } else if (element.isJsonObject()) {
JsonObject object = element.getAsJsonObject(); JsonObject object = element.getAsJsonObject();
visitor.startObject(object); visitor.startObject(object);
boolean isFirst = true; boolean isFirst = true;

View File

@ -78,7 +78,7 @@ private JsonNull JsonNull() :
Token t; Token t;
} }
{ {
t = <NULL> { return JsonNull.INSTANCE; } t = <NULL> { return JsonNull.createJsonNull(); }
} }
private void Members(JsonObject o) : private void Members(JsonObject o) :
@ -133,7 +133,7 @@ private JsonElement JsonValue() :
o=JsonObject() | o=JsonObject() |
o=JsonArray() | o=JsonArray() |
o=JsonBoolean() | o=JsonBoolean() |
"null" ) o=JsonNull() )
{ return o; } { return o; }
} }

View File

@ -75,6 +75,55 @@ public class MapTest extends TestCase {
String json = gson.toJson(map, typeOfMap); String json = gson.toJson(map, typeOfMap);
assertEquals("{}", json); assertEquals("{}", json);
} }
public void testMapDeserializationEmpty() {
Type typeOfMap = new TypeToken<Map<String, Integer>>() {}.getType();
Map<String, Integer> map = gson.fromJson("{}", typeOfMap);
assertTrue(map.isEmpty());
}
public void testMapSerializationWithNullValue() {
Map<String, Integer> map = new LinkedHashMap<String, Integer>();
map.put("abc", null);
Type typeOfMap = new TypeToken<Map<String, Integer>>() {}.getType();
String json = gson.toJson(map, typeOfMap);
// Maps are represented as JSON objects, so ignoring null field
assertEquals("{}", json);
}
public void testMapDeserializationWithNullValue() {
Type typeOfMap = new TypeToken<Map<String, Integer>>() {}.getType();
Map<String, Integer> map = gson.fromJson("{\"abc\":null}", typeOfMap);
assertEquals(1, map.size());
assertNull(map.get("abc"));
}
public void testMapSerializationWithNullValueButSerializeNulls() {
gson = new GsonBuilder().serializeNulls().create();
Map<String, Integer> map = new LinkedHashMap<String, Integer>();
map.put("abc", null);
Type typeOfMap = new TypeToken<Map<String, Integer>>() {}.getType();
String json = gson.toJson(map, typeOfMap);
assertEquals("{\"abc\":null}", json);
}
public void testMapSerializationWithNullKey() {
Map<String, Integer> map = new LinkedHashMap<String, Integer>();
map.put(null, 123);
Type typeOfMap = new TypeToken<Map<String, Integer>>() {}.getType();
String json = gson.toJson(map, typeOfMap);
assertEquals("{\"null\":123}", json);
}
public void testMapDeserializationWithNullKey() {
Type typeOfMap = new TypeToken<Map<String, Integer>>() {}.getType();
Map<String, Integer> map = gson.fromJson("{\"null\":123}", typeOfMap);
assertEquals(1, map.size());
assertNull(map.get(null));
}
public void testParameterizedMapSubclassSerialization() { public void testParameterizedMapSubclassSerialization() {
MyParameterizedMap<String, String> map = new MyParameterizedMap<String, String>(); MyParameterizedMap<String, String> map = new MyParameterizedMap<String, String>();