Skip to content
This repository was archived by the owner on Jan 20, 2025. It is now read-only.

Commit 849224b

Browse files
committed
Add support for 'collection from single value' feature
1 parent 5d9c8ce commit 849224b

File tree

7 files changed

+94
-27
lines changed

7 files changed

+94
-27
lines changed

release-notes/VERSION

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ Version: 2.3.0 (xx-Oct-2013)
33

44
#29: Empty ImmutableMap not deserialized correctly, when type info included
55
(reported by pbergn@github)
6+
- Add support for `DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY` for
7+
`ImmutableSet` and `MultiSet`
68

79
------------------------------------------------------------------------
810
=== History: ===

src/main/java/com/fasterxml/jackson/datatype/guava/deser/GuavaCollectionDeserializer.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import java.io.IOException;
44

55
import com.fasterxml.jackson.core.*;
6-
76
import com.fasterxml.jackson.databind.*;
87
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
98
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
@@ -99,11 +98,15 @@ public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
9998
public T deserialize(JsonParser jp, DeserializationContext ctxt)
10099
throws IOException, JsonProcessingException
101100
{
102-
// Ok: must point to START_ARRAY
103-
if (jp.getCurrentToken() != JsonToken.START_ARRAY) {
104-
throw ctxt.mappingException(_containerType.getRawClass());
101+
// Should usually point to START_ARRAY
102+
if (jp.isExpectedStartArrayToken()) {
103+
return _deserializeContents(jp, ctxt);
104+
}
105+
// But may support implicit arrays from single values?
106+
if (ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)) {
107+
return _deserializeFromSingleValue(jp, ctxt);
105108
}
106-
return _deserializeContents(jp, ctxt);
109+
throw ctxt.mappingException(_containerType.getRawClass());
107110
}
108111

109112
/*
@@ -114,4 +117,13 @@ public T deserialize(JsonParser jp, DeserializationContext ctxt)
114117

115118
protected abstract T _deserializeContents(JsonParser jp, DeserializationContext ctxt)
116119
throws IOException, JsonProcessingException;
120+
121+
/**
122+
* Method used to support implicit coercion from a single non-array value
123+
* into single-element collection.
124+
*
125+
* @since 2.3
126+
*/
127+
protected abstract T _deserializeFromSingleValue(JsonParser jp, DeserializationContext ctxt)
128+
throws IOException, JsonProcessingException;
117129
}

src/main/java/com/fasterxml/jackson/datatype/guava/deser/GuavaImmutableCollectionDeserializer.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,26 @@ protected T _deserializeContents(JsonParser jp, DeserializationContext ctxt)
5151
T collection = (T) builder.build();
5252
return collection;
5353
}
54+
55+
@Override
56+
protected T _deserializeFromSingleValue(JsonParser jp, DeserializationContext ctxt)
57+
throws IOException, JsonProcessingException
58+
{
59+
JsonDeserializer<?> valueDes = _valueDeserializer;
60+
final TypeDeserializer typeDeser = _typeDeserializerForValue;
61+
JsonToken t = jp.getCurrentToken();
62+
63+
Object value;
64+
65+
if (t == JsonToken.VALUE_NULL) {
66+
value = null;
67+
} else if (typeDeser == null) {
68+
value = valueDes.deserialize(jp, ctxt);
69+
} else {
70+
value = valueDes.deserializeWithType(jp, ctxt, typeDeser);
71+
}
72+
@SuppressWarnings("unchecked")
73+
T result = (T) createBuilder().add(value).build();
74+
return result;
75+
}
5476
}

src/main/java/com/fasterxml/jackson/datatype/guava/deser/GuavaMultisetDeserializer.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
import com.fasterxml.jackson.databind.type.CollectionType;
1212
import com.google.common.collect.Multiset;
1313

14-
abstract class GuavaMultisetDeserializer<T extends Multiset<Object>> extends GuavaCollectionDeserializer<T>
14+
abstract class GuavaMultisetDeserializer<T extends Multiset<Object>>
15+
extends GuavaCollectionDeserializer<T>
1516
{
1617
private static final long serialVersionUID = 1L;
1718

@@ -44,4 +45,26 @@ protected T _deserializeContents(JsonParser jp, DeserializationContext ctxt) thr
4445
return set;
4546
}
4647

48+
@Override
49+
protected T _deserializeFromSingleValue(JsonParser jp, DeserializationContext ctxt)
50+
throws IOException, JsonProcessingException
51+
{
52+
JsonDeserializer<?> valueDes = _valueDeserializer;
53+
final TypeDeserializer typeDeser = _typeDeserializerForValue;
54+
JsonToken t = jp.getCurrentToken();
55+
56+
Object value;
57+
58+
if (t == JsonToken.VALUE_NULL) {
59+
value = null;
60+
} else if (typeDeser == null) {
61+
value = valueDes.deserialize(jp, ctxt);
62+
} else {
63+
value = valueDes.deserializeWithType(jp, ctxt, typeDeser);
64+
}
65+
T result = createMultiset();
66+
result.add(value);
67+
return result;
68+
}
69+
4770
}

src/test/java/com/fasterxml/jackson/datatype/guava/TestFluentIterable.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ public class TestFluentIterable extends BaseTest
1414
{
1515
private final ObjectMapper MAPPER = mapperWithModule();
1616

17-
1817
FluentIterable<Integer> createFluentIterable() {
1918
return new FluentIterable<Integer>() {
2019
private final Iterable<Integer> _iterable = Sets.newHashSet(1, 2, 3);
@@ -33,17 +32,13 @@ public Iterator<Integer> iterator() {
3332
public void testSerializationWithoutModule() throws Exception {
3433
ObjectMapper mapper = new ObjectMapper();
3534
Iterable<Integer> fluentIterable = createFluentIterable();
36-
3735
String json = mapper.writeValueAsString(fluentIterable);
38-
3936
assertEquals("{\"empty\":false}", json);
4037
}
4138

4239
public void testSerialization() throws Exception {
4340
Iterable<Integer> fluentIterable = createFluentIterable();
44-
4541
String json = MAPPER.writeValueAsString(fluentIterable);
46-
4742
assertEquals("[1,2,3]", json);
4843
}
4944

src/test/java/com/fasterxml/jackson/datatype/guava/TestImmutables.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import com.fasterxml.jackson.annotation.JsonTypeInfo;
66
import com.fasterxml.jackson.core.type.TypeReference;
7+
import com.fasterxml.jackson.databind.DeserializationFeature;
78
import com.fasterxml.jackson.databind.JavaType;
89
import com.fasterxml.jackson.databind.JsonMappingException;
910
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -121,6 +122,16 @@ public void testImmutableSet() throws Exception
121122
assertEquals(0, set.size());
122123
}
123124

125+
public void testImmutableSetFromSingle() throws Exception
126+
{
127+
ObjectMapper mapper = mapperWithModule()
128+
.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
129+
ImmutableSet<String> set = mapper.readValue("\"abc\"",
130+
new TypeReference<ImmutableSet<String>>() { });
131+
assertEquals(1, set.size());
132+
assertTrue(set.contains("abc"));
133+
}
134+
124135
public void testTypedImmutableset() throws Exception
125136
{
126137
ImmutableSet<Integer> set;

src/test/java/com/fasterxml/jackson/datatype/guava/TestMultisets.java

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,10 @@ public void testWithoutDeserializers() throws Exception
4343
try {
4444
/*Multiset<String> set =*/ mapper.readValue("[\"abc\",\"abc\",\"foo\"]",
4545
new TypeReference<Multiset<String>>() { });
46+
fail("Should have failed");
4647
} catch (JsonMappingException e) {
4748
verifyException(e, "can not find a deserializer");
4849
}
49-
/*
50-
assertEquals(3, set.size());
51-
assertEquals(1, set.count("foo"));
52-
assertEquals(2, set.count("abc"));
53-
assertEquals(0, set.count("bar"));
54-
*/
5550
}
5651

5752
/*
@@ -60,49 +55,56 @@ public void testWithoutDeserializers() throws Exception
6055
/**********************************************************************
6156
*/
6257

58+
final ObjectMapper MAPPER = mapperWithModule();
59+
6360
public void testDefault() throws Exception
6461
{
65-
ObjectMapper mapper = mapperWithModule();
66-
Multiset<String> set = mapper.readValue("[\"abc\",\"abc\",\"foo\"]", new TypeReference<Multiset<String>>() { });
62+
Multiset<String> set = MAPPER.readValue("[\"abc\",\"abc\",\"foo\"]", new TypeReference<Multiset<String>>() { });
6763
assertEquals(3, set.size());
6864
assertEquals(1, set.count("foo"));
6965
assertEquals(2, set.count("abc"));
7066
assertEquals(0, set.count("bar"));
7167
}
7268

7369
public void testLinkedHashMultiset() throws Exception {
74-
ObjectMapper mapper = mapperWithModule();
75-
LinkedHashMultiset<String> set = mapper.readValue("[\"abc\",\"abc\",\"foo\"]", new TypeReference<LinkedHashMultiset<String>>() { });
70+
LinkedHashMultiset<String> set = MAPPER.readValue("[\"abc\",\"abc\",\"foo\"]", new TypeReference<LinkedHashMultiset<String>>() { });
7671
assertEquals(3, set.size());
7772
assertEquals(1, set.count("foo"));
7873
assertEquals(2, set.count("abc"));
7974
assertEquals(0, set.count("bar"));
8075
}
8176

8277
public void testHashMultiset() throws Exception {
83-
ObjectMapper mapper = mapperWithModule();
84-
HashMultiset<String> set = mapper.readValue("[\"abc\",\"abc\",\"foo\"]", new TypeReference<HashMultiset<String>>() { });
78+
HashMultiset<String> set = MAPPER.readValue("[\"abc\",\"abc\",\"foo\"]", new TypeReference<HashMultiset<String>>() { });
8579
assertEquals(3, set.size());
8680
assertEquals(1, set.count("foo"));
8781
assertEquals(2, set.count("abc"));
8882
assertEquals(0, set.count("bar"));
8983
}
9084

9185
public void testTreeMultiset() throws Exception {
92-
ObjectMapper mapper = mapperWithModule();
93-
TreeMultiset<String> set = mapper.readValue("[\"abc\",\"abc\",\"foo\"]", new TypeReference<TreeMultiset<String>>() { });
86+
TreeMultiset<String> set = MAPPER.readValue("[\"abc\",\"abc\",\"foo\"]", new TypeReference<TreeMultiset<String>>() { });
9487
assertEquals(3, set.size());
9588
assertEquals(1, set.count("foo"));
9689
assertEquals(2, set.count("abc"));
9790
assertEquals(0, set.count("bar"));
9891
}
9992

10093
public void testImmutableMultiset() throws Exception {
101-
ObjectMapper mapper = mapperWithModule();
102-
ImmutableMultiset<String> set = mapper.readValue("[\"abc\",\"abc\",\"foo\"]", new TypeReference<ImmutableMultiset<String>>() { });
94+
ImmutableMultiset<String> set = MAPPER.readValue("[\"abc\",\"abc\",\"foo\"]", new TypeReference<ImmutableMultiset<String>>() { });
10395
assertEquals(3, set.size());
10496
assertEquals(1, set.count("foo"));
10597
assertEquals(2, set.count("abc"));
10698
assertEquals(0, set.count("bar"));
10799
}
100+
101+
public void testFromSingle() throws Exception
102+
{
103+
ObjectMapper mapper = mapperWithModule()
104+
.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
105+
Multiset<String> set = mapper.readValue("\"abc\"",
106+
new TypeReference<Multiset<String>>() { });
107+
assertEquals(1, set.size());
108+
assertTrue(set.contains("abc"));
109+
}
108110
}

0 commit comments

Comments
 (0)