Skip to content

Commit a29cda6

Browse files
Add deserialization support for Table<R, C, V> (#163)
1 parent b9adb6d commit a29cda6

18 files changed

+705
-195
lines changed

guava/src/main/java/com/fasterxml/jackson/datatype/guava/GuavaDeserializers.java

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.fasterxml.jackson.datatype.guava;
22

3+
import com.fasterxml.jackson.datatype.guava.deser.table.HashBasedTableDeserializer;
4+
import com.fasterxml.jackson.datatype.guava.deser.table.ImmutableTableDeserializer;
5+
import com.fasterxml.jackson.datatype.guava.deser.table.TreeBasedTableDeserializer;
36
import java.io.Serializable;
47

58
import com.google.common.base.Optional;
@@ -259,8 +262,15 @@ public JsonDeserializer<?> findMapLikeDeserializer(MapLikeType type,
259262
}
260263

261264
if (Table.class.isAssignableFrom(raw)) {
262-
// !!! TODO
265+
if (HashBasedTable.class.isAssignableFrom(raw)) {
266+
return new HashBasedTableDeserializer(type);
267+
}
268+
if (TreeBasedTable.class.isAssignableFrom(raw)) {
269+
return new TreeBasedTableDeserializer(type);
270+
}
271+
return new ImmutableTableDeserializer(type);
263272
}
273+
264274
// @since 2.16 : support Cache deserialization
265275
java.util.Optional<JsonDeserializer<?>> cacheDeserializer = findCacheDeserializer(raw, type, config,
266276
beanDesc, keyDeserializer, elementTypeDeserializer, elementDeserializer);
@@ -315,7 +325,7 @@ public JsonDeserializer<?> findReferenceDeserializer(ReferenceType refType,
315325
public JsonDeserializer<?> findBeanDeserializer(final JavaType type, DeserializationConfig config,
316326
BeanDescription beanDesc)
317327
{
318-
if (RangeSet.class.isAssignableFrom(type.getRawClass())) {
328+
if (type.isTypeOrSubTypeOf(RangeSet.class)) {
319329
return new RangeSetDeserializer();
320330
}
321331
if (type.hasRawClass(Range.class)) {

guava/src/main/java/com/fasterxml/jackson/datatype/guava/GuavaSerializers.java

+13-14
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public Iterable<?> convert(Object value) {
4747
}
4848

4949
@Override
50-
public JsonSerializer<?> findReferenceSerializer(SerializationConfig config,
50+
public JsonSerializer<?> findReferenceSerializer(SerializationConfig config,
5151
ReferenceType refType, BeanDescription beanDesc,
5252
TypeSerializer contentTypeSerializer, JsonSerializer<Object> contentValueSerializer)
5353
{
@@ -64,32 +64,28 @@ public JsonSerializer<?> findReferenceSerializer(SerializationConfig config,
6464
@Override
6565
public JsonSerializer<?> findSerializer(SerializationConfig config, JavaType type, BeanDescription beanDesc)
6666
{
67-
Class<?> raw = type.getRawClass();
68-
if (RangeSet.class.isAssignableFrom(raw)) {
67+
if (type.isTypeOrSubTypeOf(RangeSet.class)) {
6968
return new RangeSetSerializer();
7069
}
71-
if (Range.class.isAssignableFrom(raw)) {
70+
if (type.isTypeOrSubTypeOf(Range.class)) {
7271
return new RangeSerializer(_findDeclared(type, Range.class));
7372
}
74-
if (Table.class.isAssignableFrom(raw)) {
75-
return new TableSerializer(_findDeclared(type, Table.class));
76-
}
7773

7874
// since 2.4
79-
if (HostAndPort.class.isAssignableFrom(raw)) {
75+
if (type.isTypeOrSubTypeOf(HostAndPort.class)) {
8076
return ToStringSerializer.instance;
8177
}
82-
if (InternetDomainName.class.isAssignableFrom(raw)) {
78+
if (type.isTypeOrSubTypeOf(InternetDomainName.class)) {
8379
return ToStringSerializer.instance;
8480
}
8581
// not sure how useful, but why not?
86-
if (CacheBuilderSpec.class.isAssignableFrom(raw) || CacheBuilder.class.isAssignableFrom(raw)) {
82+
if (type.isTypeOrSubTypeOf(CacheBuilderSpec.class) || type.isTypeOrSubTypeOf(CacheBuilder.class)) {
8783
return ToStringSerializer.instance;
8884
}
89-
if (HashCode.class.isAssignableFrom(raw)) {
85+
if (type.isTypeOrSubTypeOf(HashCode.class)) {
9086
return ToStringSerializer.instance;
9187
}
92-
if (FluentIterable.class.isAssignableFrom(raw)) {
88+
if (type.isTypeOrSubTypeOf(FluentIterable.class)) {
9389
JavaType iterableType = _findDeclared(type, Iterable.class);
9490
return new StdDelegatingSerializer(FluentConverter.instance, iterableType, null);
9591
}
@@ -101,7 +97,7 @@ public JsonSerializer<?> findMapLikeSerializer(SerializationConfig config,
10197
MapLikeType type, BeanDescription beanDesc, JsonSerializer<Object> keySerializer,
10298
TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer)
10399
{
104-
if (Multimap.class.isAssignableFrom(type.getRawClass())) {
100+
if (type.isTypeOrSubTypeOf(Multimap.class)) {
105101
final AnnotationIntrospector intr = config.getAnnotationIntrospector();
106102
Object filterId = intr.findFilterId((Annotated)beanDesc.getClassInfo());
107103
JsonIgnoreProperties.Value ignorals = config.getDefaultPropertyIgnorals(Multimap.class,
@@ -110,7 +106,7 @@ public JsonSerializer<?> findMapLikeSerializer(SerializationConfig config,
110106
return new MultimapSerializer(type, beanDesc,
111107
keySerializer, elementTypeSerializer, elementValueSerializer, ignored, filterId);
112108
}
113-
if (Cache.class.isAssignableFrom(type.getRawClass())) {
109+
if (type.isTypeOrSubTypeOf(Cache.class)) {
114110
final AnnotationIntrospector intr = config.getAnnotationIntrospector();
115111
Object filterId = intr.findFilterId((Annotated)beanDesc.getClassInfo());
116112
JsonIgnoreProperties.Value ignorals = config.getDefaultPropertyIgnorals(Cache.class,
@@ -119,6 +115,9 @@ public JsonSerializer<?> findMapLikeSerializer(SerializationConfig config,
119115
return new CacheSerializer(type, beanDesc,
120116
keySerializer, elementTypeSerializer, elementValueSerializer, ignored, filterId);
121117
}
118+
if (type.isTypeOrSubTypeOf(Table.class)) {
119+
return new TableSerializer(type);
120+
}
122121
return null;
123122
}
124123

guava/src/main/java/com/fasterxml/jackson/datatype/guava/GuavaTypeModifier.java

+13
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,19 @@ public JavaType modifyType(JavaType type, Type jdkType, TypeBindings bindings, T
5353
if (raw == Optional.class) {
5454
return ReferenceType.upgradeFrom(type, type.containedTypeOrUnknown(0));
5555
}
56+
if (raw == Table.class) {
57+
MapLikeType columnValueType =
58+
MapLikeType.upgradeFrom(
59+
type,
60+
type.containedTypeOrUnknown(1),
61+
type.containedTypeOrUnknown(2)
62+
);
63+
return MapLikeType.upgradeFrom(
64+
type,
65+
type.containedTypeOrUnknown(0),
66+
columnValueType
67+
);
68+
}
5669
return type;
5770
}
5871
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ protected T _deserializeFromSingleValue(JsonParser p, DeserializationContext ctx
172172
{
173173
final JsonDeserializer<?> valueDes = _valueDeserializer;
174174
final TypeDeserializer typeDeser = _valueTypeDeserializer;
175-
final JsonToken t = p.getCurrentToken();
175+
final JsonToken t = p.currentToken();
176176

177177
final Object value;
178178

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ protected T _deserializeEntries(JsonParser p, DeserializationContext ctxt)
4343
final TypeDeserializer typeDeser = _valueTypeDeserializer;
4444

4545
ImmutableMap.Builder<Object, Object> builder = createBuilder();
46-
for (; p.getCurrentToken() == JsonToken.FIELD_NAME; p.nextToken()) {
46+
for (; p.currentToken() == JsonToken.FIELD_NAME; p.nextToken()) {
4747
// Must point to field name now
4848
String fieldName = p.currentName();
4949
Object key = (keyDes == null) ? fieldName : keyDes.deserializeKey(fieldName, ctxt);

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ public T deserialize(JsonParser p, DeserializationContext ctxt)
158158
throws IOException
159159
{
160160
// Ok: must point to START_OBJECT or FIELD_NAME
161-
JsonToken t = p.getCurrentToken();
161+
JsonToken t = p.currentToken();
162162
if (t == JsonToken.START_OBJECT) { // If START_OBJECT, move to next; may also be END_OBJECT
163163
t = p.nextToken();
164164
}

guava/src/main/java/com/fasterxml/jackson/datatype/guava/deser/cache/GuavaCacheDeserializer.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ private T deserializeContents(JsonParser p, DeserializationContext ctxt)
160160
}
161161

162162
private void expect(JsonParser p, JsonToken token) throws IOException {
163-
if (p.getCurrentToken() != token) {
163+
if (p.currentToken() != token) {
164164
throw new JsonMappingException(p, "Expecting " + token + " to start `Cache` value, found " + p.currentToken(),
165165
p.currentLocation());
166166
}

guava/src/main/java/com/fasterxml/jackson/datatype/guava/deser/multimap/GuavaMultimapDeserializer.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ private Object getCurrentTokenValue(JsonParser p, DeserializationContext ctxt)
272272
}
273273

274274
private void expect(JsonParser p, JsonToken token) throws IOException {
275-
if (p.getCurrentToken() != token) {
275+
if (p.currentToken() != token) {
276276
throw new JsonMappingException(p, "Expecting " + token + " to start `MultiMap` value, found " + p.currentToken(),
277277
p.currentLocation());
278278
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.fasterxml.jackson.datatype.guava.deser.table;
2+
3+
import com.fasterxml.jackson.databind.JsonDeserializer;
4+
import com.fasterxml.jackson.databind.KeyDeserializer;
5+
import com.fasterxml.jackson.databind.deser.NullValueProvider;
6+
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
7+
import com.fasterxml.jackson.databind.type.MapLikeType;
8+
import com.google.common.collect.HashBasedTable;
9+
10+
/**
11+
* Provides deserialization for the Guava HashBasedTable class.
12+
*
13+
* @author Abhishekkr3003
14+
*/
15+
public class HashBasedTableDeserializer
16+
extends MutableTableDeserializer<HashBasedTable<Object, Object, Object>> {
17+
private static final long serialVersionUID = 1L;
18+
19+
public HashBasedTableDeserializer(MapLikeType type) {
20+
super(type);
21+
}
22+
23+
public HashBasedTableDeserializer(MapLikeType type, KeyDeserializer rowDeserializer,
24+
KeyDeserializer columnDeserializer, TypeDeserializer elementTypeDeserializer,
25+
JsonDeserializer<?> elementDeserializer, NullValueProvider nvp) {
26+
super(type, rowDeserializer, columnDeserializer, elementTypeDeserializer,
27+
elementDeserializer, nvp
28+
);
29+
}
30+
31+
@Override
32+
protected HashBasedTable<Object, Object, Object> createTable() {
33+
return HashBasedTable.create();
34+
}
35+
36+
@Override
37+
protected JsonDeserializer<?> _createContextual(MapLikeType type,
38+
KeyDeserializer rowDeserializer,
39+
KeyDeserializer columnDeserializer, TypeDeserializer typeDeserializer,
40+
JsonDeserializer<?> elementDeserializer, NullValueProvider nvp) {
41+
return new HashBasedTableDeserializer(type,
42+
rowDeserializer, columnDeserializer, typeDeserializer, elementDeserializer, nvp);
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package com.fasterxml.jackson.datatype.guava.deser.table;
2+
3+
import com.fasterxml.jackson.core.JsonParser;
4+
import com.fasterxml.jackson.core.JsonToken;
5+
import com.fasterxml.jackson.databind.DeserializationContext;
6+
import com.fasterxml.jackson.databind.JsonDeserializer;
7+
import com.fasterxml.jackson.databind.JsonMappingException;
8+
import com.fasterxml.jackson.databind.KeyDeserializer;
9+
import com.fasterxml.jackson.databind.deser.NullValueProvider;
10+
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
11+
import com.fasterxml.jackson.databind.type.MapLikeType;
12+
import com.fasterxml.jackson.databind.util.AccessPattern;
13+
import com.google.common.collect.ImmutableMap;
14+
import com.google.common.collect.ImmutableTable;
15+
import java.io.IOException;
16+
17+
/**
18+
* Provides deserialization for the Guava ImmutableTable class.
19+
*
20+
* @author Abhishekkr3003
21+
*/
22+
public class ImmutableTableDeserializer
23+
extends TableDeserializer<ImmutableTable<Object, Object, Object>> {
24+
private static final long serialVersionUID = 2L;
25+
26+
public ImmutableTableDeserializer(MapLikeType type) {
27+
super(type);
28+
}
29+
30+
protected ImmutableTableDeserializer(MapLikeType type, KeyDeserializer rowDeserializer,
31+
KeyDeserializer colDeserializer, JsonDeserializer<?> valueDeserializer,
32+
TypeDeserializer valueTypeDeserializer, NullValueProvider nuller) {
33+
super(type, rowDeserializer, colDeserializer, valueTypeDeserializer, valueDeserializer,
34+
nuller
35+
);
36+
}
37+
38+
@Override
39+
public AccessPattern getEmptyAccessPattern() {
40+
// immutable, hence:
41+
return AccessPattern.CONSTANT;
42+
}
43+
44+
45+
@Override
46+
public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException {
47+
return ImmutableMap.of();
48+
}
49+
50+
protected ImmutableTable.Builder<Object, Object, Object> createBuilder() {
51+
return ImmutableTable.builder();
52+
}
53+
54+
@Override
55+
public ImmutableTable<Object, Object, Object> deserialize(JsonParser p,
56+
DeserializationContext ctxt) throws IOException {
57+
ImmutableTable.Builder<Object, Object, Object> table = createBuilder();
58+
59+
JsonToken currToken = p.currentToken();
60+
if (currToken != JsonToken.FIELD_NAME && currToken != JsonToken.END_OBJECT) {
61+
expect(p, JsonToken.START_OBJECT);
62+
currToken = p.nextToken();
63+
}
64+
65+
for (; currToken == JsonToken.FIELD_NAME; currToken = p.nextToken()) {
66+
final Object rowKey;
67+
if (_rowDeserializer != null) {
68+
rowKey = _rowDeserializer.deserializeKey(p.currentName(), ctxt);
69+
} else {
70+
rowKey = p.currentName();
71+
}
72+
73+
p.nextToken();
74+
expect(p, JsonToken.START_OBJECT);
75+
76+
for (
77+
currToken = p.nextToken(); currToken == JsonToken.FIELD_NAME;
78+
currToken = p.nextToken()) {
79+
final Object colKey;
80+
if (_colDeserializer != null) {
81+
colKey = _colDeserializer.deserializeKey(p.currentName(), ctxt);
82+
} else {
83+
colKey = p.currentName();
84+
}
85+
86+
p.nextToken();
87+
88+
final Object value;
89+
if (p.currentToken() == JsonToken.VALUE_NULL) {
90+
if (_skipNullValues) {
91+
continue;
92+
}
93+
value = _nullProvider.getNullValue(ctxt);
94+
} else if (_valueTypeDeserializer != null) {
95+
value = _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
96+
} else {
97+
value = _valueDeserializer.deserialize(p, ctxt);
98+
}
99+
table.put(rowKey, colKey, value);
100+
}
101+
expect(p, JsonToken.END_OBJECT);
102+
}
103+
return table.build();
104+
}
105+
106+
@Override
107+
protected JsonDeserializer<?> _createContextual(MapLikeType type,
108+
KeyDeserializer rowDeserializer,
109+
KeyDeserializer columnDeserializer, TypeDeserializer valueTypeDeserializer,
110+
JsonDeserializer<?> valueDeserializer, NullValueProvider nullValueProvider) {
111+
return new ImmutableTableDeserializer(type, rowDeserializer, columnDeserializer,
112+
valueDeserializer, valueTypeDeserializer, nullValueProvider
113+
);
114+
}
115+
}

0 commit comments

Comments
 (0)