Skip to content
This repository has been archived by the owner on Jan 22, 2019. It is now read-only.

Support Tables #62

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.google.common.hash.HashCode;
import com.google.common.net.HostAndPort;
import com.google.common.net.InternetDomainName;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.Deserializers;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
Expand Down Expand Up @@ -206,10 +205,6 @@ public JsonDeserializer<?> findMapLikeDeserializer(MapLikeType type,
elementTypeDeserializer, elementDeserializer);
}

if (Table.class.isAssignableFrom(raw)) {
// !!! TODO
}

return null;
}

Expand Down Expand Up @@ -241,6 +236,15 @@ public JsonDeserializer<?> findBeanDeserializer(final JavaType type, Deserializa
if (raw == HashCode.class) {
return HashCodeDeserializer.std;
}

// Tables don't fit in the MapLike method because they have three type parameters
// since 2.5.1
if (Table.class.isAssignableFrom(raw)) {
if (ImmutableTable.class.isAssignableFrom(raw)) {
return new ImmutableTableDeserializer(type);
}
// TODO: Other Table types
}
return super.findBeanDeserializer(type, config, beanDesc);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Multimap;
import com.google.common.collect.Range;
import com.google.common.collect.Table;
import com.google.common.hash.HashCode;
import com.google.common.net.HostAndPort;
import com.google.common.net.InternetDomainName;
Expand All @@ -24,6 +25,7 @@
import com.fasterxml.jackson.datatype.guava.ser.GuavaOptionalSerializer;
import com.fasterxml.jackson.datatype.guava.ser.MultimapSerializer;
import com.fasterxml.jackson.datatype.guava.ser.RangeSerializer;
import com.fasterxml.jackson.datatype.guava.ser.TableSerializer;

public class GuavaSerializers extends Serializers.Base
{
Expand Down Expand Up @@ -70,6 +72,12 @@ public JsonSerializer<?> findSerializer(SerializationConfig config, JavaType typ
// JavaType delegate = config.getTypeFactory().constructParametrizedType(FluentIterable.class, Iterable.class, vt);
return new StdDelegatingSerializer(FluentConverter.instance, delegate, null);
}

// since 2.5.1
if (Table.class.isAssignableFrom(raw)) {
return new TableSerializer(config, type);
}

return super.findSerializer(config, type, beanDesc);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Multimap;
import com.google.common.collect.Range;
import com.google.common.collect.Table;

public class GuavaTypeModifier extends TypeModifier
{
Expand Down Expand Up @@ -82,6 +83,13 @@ public JavaType modifyType(JavaType type, Type jdkType, TypeBindings context, Ty
return typeFactory.constructParametrizedType(raw, target, t);
}
}
if (Table.class.isAssignableFrom(type.getRawClass())) {
final JavaType rowType = (type.containedType(0)) == null ? typeFactory.constructType(Object.class) : type.containedType(0);
final JavaType columnType = (type.containedType(1)) == null ? typeFactory.constructType(Object.class) : type.containedType(1);
final JavaType contentType = (type.containedType(2)) == null ? typeFactory.constructType(Object.class) : type.containedType(2);

return typeFactory.constructParametrizedType(type.getRawClass(), type.getRawClass(), rowType, columnType, contentType);
}
return type;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.fasterxml.jackson.datatype.guava.deser;

import java.io.IOException;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;

import com.google.common.collect.ImmutableTable;

abstract class GuavaImmutableTableDeserializer<T extends ImmutableTable<Object, Object, Object>> extends GuavaTableDeserializer<T>
{
GuavaImmutableTableDeserializer( final JavaType javaType )
{
super(javaType);
}

protected abstract ImmutableTable.Builder<Object, Object, Object> createBuilder();

@Override
protected T _deserializeEntries( final JsonParser jp, final DeserializationContext ctxt ) throws IOException, JsonProcessingException
{
final KeyDeserializer rowKeyDes = this._rowKeyDeserializer;
final KeyDeserializer columnKeyDes = this._columnKeyDeserializer;
final JsonDeserializer<?> valueDes = this._valueDeserializer;
final TypeDeserializer typeDeser = this._typeDeserializerForValue;

final ImmutableTable.Builder<Object, Object, Object> builder = this.createBuilder();
for ( ; jp.getCurrentToken() == JsonToken.FIELD_NAME; jp.nextToken() ) {
// Must point to row now
final String rowName = jp.getCurrentName();
final Object row = (rowKeyDes == null) ? rowName : rowKeyDes.deserializeKey(rowName, ctxt);
// And then the {column => value} start token...
jp.nextToken();
// Now pointing to column
jp.nextToken();

for ( ; jp.getCurrentToken() == JsonToken.FIELD_NAME; jp.nextToken() ) {
// Must point to column now
final String columnName = jp.getCurrentName();
final Object column = (columnKeyDes == null) ? columnName : columnKeyDes.deserializeKey(columnName, ctxt);
// And then the value...
final JsonToken tValue = jp.nextToken();
// 28-Nov-2010, tatu: Should probably support "ignorable properties" in future...
Object value;
if (tValue == JsonToken.VALUE_NULL) {
value = null;
}
else {
value = (typeDeser == null) ? valueDes.deserialize(jp, ctxt) : valueDes.deserializeWithType(jp, ctxt, typeDeser);
builder.put(row, column, value);
}
}
}
// No class outside of the package will be able to subclass us,
// and we provide the proper builder for the subclasses we implement.
@SuppressWarnings( "unchecked" )
final T table = (T) builder.build();
return table;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package com.fasterxml.jackson.datatype.guava.deser;

import java.io.IOException;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;

public abstract class GuavaTableDeserializer<T> extends JsonDeserializer<T> implements ContextualDeserializer
{
protected final JavaType _javaType;

/**
* Row key deserializer used, if not null. If null, String from JSON content is used as is.
*/
protected KeyDeserializer _rowKeyDeserializer;

/**
* Column key deserializer used, if not null. If null, String from JSON content is used as is.
*/
protected KeyDeserializer _columnKeyDeserializer;

/**
* Value deserializer.
*/
protected JsonDeserializer<?> _valueDeserializer;

/**
* If value instances have polymorphic type information, this is the type deserializer that can handle it.
*/
protected TypeDeserializer _typeDeserializerForValue;

/*
* Life-cycle
*/

protected GuavaTableDeserializer( final JavaType javaType )
{
this._javaType = javaType;
}

/**
* Overridable fluent factory method used for creating contextual instances.
*/
public abstract GuavaTableDeserializer<T> withResolved( final KeyDeserializer rowKeyDeser,
final KeyDeserializer columnKeyDeser,
final TypeDeserializer typeDeser,
final JsonDeserializer<?> valueDeser );

/*
* Validation, post-processing
*/

/**
* Method called to finalize setup of this deserializer, after deserializer itself has been registered. This is needed to handle recursive and
* transitive dependencies.
*/
@Override
public JsonDeserializer<?> createContextual( final DeserializationContext ctxt, final BeanProperty property ) throws JsonMappingException
{
KeyDeserializer rowKeyDeser = this._rowKeyDeserializer;
KeyDeserializer columnKeyDeser = this._columnKeyDeserializer;
JsonDeserializer<?> deser = this._valueDeserializer;
TypeDeserializer typeDeser = this._typeDeserializerForValue;
// Do we need any contextualization?
if ((rowKeyDeser != null) && (columnKeyDeser != null) && (deser != null) && (typeDeser == null)) { // nope
return this;
}
if (rowKeyDeser == null) {
rowKeyDeser = ctxt.findKeyDeserializer(this._javaType.containedType(0), property);
}
if (columnKeyDeser == null) {
columnKeyDeser = ctxt.findKeyDeserializer(this._javaType.containedType(1), property);
}
if (deser == null) {
deser = ctxt.findContextualValueDeserializer(this._javaType.containedType(2), property);
}
if (typeDeser != null) {
typeDeser = typeDeser.forProperty(property);
}
return this.withResolved(rowKeyDeser, columnKeyDeser, typeDeser, deser);
}

/*
* Deserialization interface
*/

/**
* Base implementation that does not assume specific type inclusion mechanism. Sub-classes are expected to override this method if they are to
* handle type information.
*/
@Override
public Object deserializeWithType( final JsonParser jp, final DeserializationContext ctxt, final TypeDeserializer typeDeserializer )
throws IOException, JsonProcessingException
{
// note: call "...FromObject" because expected output structure
// for value is JSON Object (regardless of contortions used for type id)
return typeDeserializer.deserializeTypedFromObject(jp, ctxt);
}

@Override
public T deserialize( final JsonParser jp, final DeserializationContext ctxt ) throws IOException, JsonProcessingException
{
// Ok: must point to START_OBJECT or FIELD_NAME
JsonToken t = jp.getCurrentToken();
if (t == JsonToken.START_OBJECT) { // If START_OBJECT, move to next; may also be END_OBJECT
t = jp.nextToken();
}
if (t != JsonToken.FIELD_NAME && t != JsonToken.END_OBJECT) {
throw ctxt.mappingException(this._javaType.getRawClass());
}
return this._deserializeEntries(jp, ctxt);
}

/*
* Abstract methods for impl classes
*/

protected abstract T _deserializeEntries( final JsonParser jp, final DeserializationContext ctxt ) throws IOException, JsonProcessingException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.fasterxml.jackson.datatype.guava.deser;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;

import com.google.common.collect.ImmutableTable;

public class ImmutableTableDeserializer extends GuavaImmutableTableDeserializer<ImmutableTable<Object, Object, Object>>
{
public ImmutableTableDeserializer( final JavaType javaType )
{
super(javaType);
}

public ImmutableTableDeserializer( final JavaType javaType,
final KeyDeserializer rowKeyDeser,
final KeyDeserializer columnKeyDeser,
final TypeDeserializer typeDeser,
final JsonDeserializer<?> deser )
{
super(javaType);
this._rowKeyDeserializer = rowKeyDeser;
this._columnKeyDeserializer = columnKeyDeser;
this._valueDeserializer = deser;
this._typeDeserializerForValue = typeDeser;
}

@Override
public ImmutableTableDeserializer withResolved( final KeyDeserializer rowKeyDeser,
final KeyDeserializer columnKeyDeser,
final TypeDeserializer typeDeser,
final JsonDeserializer<?> valueDeser )
{
return new ImmutableTableDeserializer(this._javaType, rowKeyDeser, columnKeyDeser, typeDeser, valueDeser);
}

@Override
protected ImmutableTable.Builder<Object, Object, Object> createBuilder()
{
return ImmutableTable.builder();
}
}
Loading