diff --git a/gson/src/main/java/com/google/gson/internal/StringMap.java b/gson/src/main/java/com/google/gson/internal/StringMap.java index 55ff016d..caa21452 100644 --- a/gson/src/main/java/com/google/gson/internal/StringMap.java +++ b/gson/src/main/java/com/google/gson/internal/StringMap.java @@ -29,7 +29,8 @@ import java.util.Set; /** * A map of strings to values. Like LinkedHashMap, this map's iteration order is - * well defined: it is the order that elements were inserted into the map. + * well defined: it is the order that elements were inserted into the map. This + * map does not support null keys. * *

This implementation was derived from Android 4.0's LinkedHashMap. */ @@ -98,32 +99,33 @@ public final class StringMap extends AbstractMap { } @Override public boolean containsKey(Object key) { - return get(key) != null; + return getEntry(key) != null; } @Override public V get(Object key) { + LinkedEntry entry = getEntry(key); + return entry != null ? entry.value : null; + } + + private LinkedEntry getEntry(Object key) { if (key == null) { return null; } - // Doug Lea's supplemental secondaryHash function (inlined) - int hash = key.hashCode(); - hash ^= (hash >>> 20) ^ (hash >>> 12); - hash ^= (hash >>> 7) ^ (hash >>> 4); - + int hash = secondaryHash(key.hashCode()); LinkedEntry[] tab = table; for (LinkedEntry e = tab[hash & (tab.length - 1)]; e != null; e = e.next) { K eKey = e.key; if (eKey == key || (e.hash == hash && key.equals(eKey))) { - return e.value; + return e; } } return null; } @Override public V put(K key, V value) { - if (key == null || value == null) { - throw new IllegalArgumentException(); + if (key == null) { + throw new NullPointerException("key == null"); } int hash = secondaryHash(key.hashCode()); @@ -311,9 +313,6 @@ public final class StringMap extends AbstractMap { } public final V setValue(V value) { - if (value == null) { - throw new IllegalArgumentException(); - } V oldValue = this.value; this.value = value; return oldValue; @@ -324,7 +323,9 @@ public final class StringMap extends AbstractMap { return false; } Entry e = (Entry) o; - return key.equals(e.getKey()) && value.equals(e.getValue()); + Object eValue = e.getValue(); + return key.equals(e.getKey()) + && (value == null ? eValue == null : value.equals(eValue)); } @Override public final int hashCode() { @@ -341,7 +342,7 @@ public final class StringMap extends AbstractMap { * exists; otherwise, returns does nothing and returns false. */ private boolean removeMapping(Object key, Object value) { - if (key == null || value == null) { + if (key == null) { return false; } @@ -350,7 +351,7 @@ public final class StringMap extends AbstractMap { int index = hash & (tab.length - 1); for (LinkedEntry e = tab[index], prev = null; e != null; prev = e, e = e.next) { if (e.hash == hash && key.equals(e.key)) { - if (!value.equals(e.value)) { + if (value == null ? e.value != null : !value.equals(e.value)) { return false; // Map has wrong value for key } if (prev == null) { diff --git a/gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java index 0f60c922..77a9a6bd 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java @@ -63,6 +63,7 @@ public final class ObjectTypeAdapter extends TypeAdapter { return list; case BEGIN_OBJECT: + // TODO: string map doesn't support null values Map map = new StringMap(); in.beginObject(); while (in.hasNext()) { diff --git a/gson/src/test/java/com/google/gson/ObjectTypeAdapterTest.java b/gson/src/test/java/com/google/gson/ObjectTypeAdapterTest.java index 65798dd7..2891bffc 100644 --- a/gson/src/test/java/com/google/gson/ObjectTypeAdapterTest.java +++ b/gson/src/test/java/com/google/gson/ObjectTypeAdapterTest.java @@ -18,6 +18,7 @@ package com.google.gson; import java.util.Arrays; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.Map; import junit.framework.TestCase; @@ -37,6 +38,18 @@ public final class ObjectTypeAdapterTest extends TestCase { Object object = new RuntimeType(); assertEquals("{'a':5,'b':[1,2,null]}", adapter.toJson(object).replace("\"", "'")); } + + public void testSerializeNullValue() throws Exception { + Map map = new LinkedHashMap(); + map.put("a", null); + assertEquals("{'a':null}", adapter.toJson(map).replace('"', '\'')); + } + + public void testDeserializeNullValue() throws Exception { + Map map = new LinkedHashMap(); + map.put("a", null); + assertEquals(map, adapter.fromJson("{\"a\":null}")); + } public void testSerializeObject() throws Exception { assertEquals("{}", adapter.toJson(new Object()));