Skip to content

Commit 64967c4

Browse files
committed
Fixed #1513
1 parent dc02cea commit 64967c4

File tree

3 files changed

+40
-5
lines changed

3 files changed

+40
-5
lines changed

release-notes/VERSION

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@ Project: jackson-databind
77

88
#935: `@JsonProperty(access = Access.READ_ONLY)` - unexpected behaviour
99
#1317: '@JsonIgnore' annotation not working with creator properties, serialization
10+
#1367: No Object Id found for an instance when using `@ConstructorProperties`
1011
#1505: @JsonEnumDefaultValue should take precedence over FAIL_ON_NUMBERS_FOR_ENUMS
1112
(suggested by Stephan S)
13+
#1506: Missing `KeyDeserializer` for `CharSequence`
14+
#1513: `MapSerializer._orderEntries()` throws NPE when operating on `ConcurrentHashMap`
15+
(reported by Sovietaced@github)
1216
- Simplified processing of class annotations (for `AnnotatedClass`) to try to
1317
solve rare concurrency problems with "root name" annotations.
1418

src/main/java/com/fasterxml/jackson/databind/ser/std/MapSerializer.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -952,7 +952,9 @@ protected Map<?,?> _orderEntries(Map<?,?> input, JsonGenerator gen,
952952
}
953953
// [databind#1411]: TreeMap does not like null key... (although note that
954954
// check above should prevent this code from being called in that case)
955-
if (input.containsKey(null)) {
955+
// [databind#153]: but, apparently, some custom Maps do manage hit this
956+
// problem.
957+
if (_hasNullKey(input)) {
956958
TreeMap<Object,Object> result = new TreeMap<Object,Object>();
957959
for (Map.Entry<?,?> entry : input.entrySet()) {
958960
Object key = entry.getKey();
@@ -967,6 +969,22 @@ protected Map<?,?> _orderEntries(Map<?,?> input, JsonGenerator gen,
967969
return new TreeMap<Object,Object>(input);
968970
}
969971

972+
/**
973+
* @since 2.8.7
974+
*/
975+
protected boolean _hasNullKey(Map<?,?> input) {
976+
// 19-Feb-2017, tatu: As per [databind#1513] there are many cases where `null`
977+
// keys are not allowed, and even attempt to check for presence can cause
978+
// problems. Without resorting to external sorting (and internal API change),
979+
// or custom sortable Map implementation (more code) we can try black- or
980+
// white-listing (that is; either skip known problem cases; or only apply for
981+
// known good cases).
982+
// While my first instinct was to do black-listing (remove Hashtable and ConcurrentHashMap),
983+
// all in all it is probably better to just white list `HashMap` (and its sub-classes).
984+
985+
return (input instanceof HashMap) && input.containsKey(null);
986+
}
987+
970988
protected void _writeNullKeyedEntry(JsonGenerator gen, SerializerProvider provider,
971989
Object suppressableValue, Object value) throws IOException
972990
{

src/test/java/com/fasterxml/jackson/databind/ser/TestMapSerialization.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -287,14 +287,27 @@ public void testNullJsonInTypedMap691() throws Exception {
287287
}
288288

289289
// [databind#1513]
290-
public void testConcurrentSkipListMap() throws Exception
290+
public void testConcurrentMaps() throws Exception
291291
{
292+
final ObjectWriter w = MAPPER.writer(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS);
293+
292294
Map<String,String> input = new ConcurrentSkipListMap<String,String>();
295+
input.put("x", "y");
293296
input.put("a", "b");
297+
String json = w.writeValueAsString(input);
298+
assertEquals(aposToQuotes("{'a':'b','x':'y'}"), json);
299+
300+
input = new ConcurrentHashMap<String,String>();
294301
input.put("x", "y");
295-
String json = MAPPER
296-
.writer().with(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)
297-
.writeValueAsString(input);
302+
input.put("a", "b");
303+
json = w.writeValueAsString(input);
304+
assertEquals(aposToQuotes("{'a':'b','x':'y'}"), json);
305+
306+
// One more: while not technically concurrent map at all, exhibits same issue
307+
input = new Hashtable<String,String>();
308+
input.put("x", "y");
309+
input.put("a", "b");
310+
json = w.writeValueAsString(input);
298311
assertEquals(aposToQuotes("{'a':'b','x':'y'}"), json);
299312
}
300313
}

0 commit comments

Comments
 (0)