Skip to content

Commit

Permalink
Fix #1385
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Sep 28, 2016
1 parent 9f01551 commit be114a9
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 61 deletions.
2 changes: 2 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Project: jackson-databind
#1384: `@JsonDeserialize(keyUsing = ...)` does not work correctly together with
DefaultTyping.NON_FINAL
(reported by Oleg Z)
#1385: Polymorphic type lost when using `@JsonValue`
(reported by TomMarkuske@github)
#1389 Problem with handling of multi-argument creator with Enums
(fix contributed by Pavel P)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.LinkedHashSet;
import java.util.Set;

import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
Expand All @@ -14,6 +15,7 @@
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonStringFormatVisitor;
import com.fasterxml.jackson.databind.jsonschema.SchemaAware;
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.BeanSerializer;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
Expand Down Expand Up @@ -44,7 +46,7 @@ public class JsonValueSerializer
protected final JsonSerializer<Object> _valueSerializer;

protected final BeanProperty _property;

/**
* This is a flag that is set in rare (?) cases where this serializer
* is used for "natural" types (boolean, int, String, double); and where
Expand Down Expand Up @@ -156,12 +158,12 @@ public JsonSerializer<?> createContextual(SerializerProvider provider,
*/

@Override
public void serialize(Object bean, JsonGenerator jgen, SerializerProvider prov) throws IOException
public void serialize(Object bean, JsonGenerator gen, SerializerProvider prov) throws IOException
{
try {
Object value = _accessorMethod.getValue(bean);
if (value == null) {
prov.defaultSerializeNull(jgen);
prov.defaultSerializeNull(gen);
return;
}
JsonSerializer<Object> ser = _valueSerializer;
Expand All @@ -174,7 +176,7 @@ public void serialize(Object bean, JsonGenerator jgen, SerializerProvider prov)
// let's cache it, may be needed soon again
ser = prov.findTypedValueSerializer(c, true, _property);
}
ser.serialize(value, jgen, prov);
ser.serialize(value, gen, prov);
} catch (IOException ioe) {
throw ioe;
} catch (Exception e) {
Expand All @@ -193,7 +195,7 @@ public void serialize(Object bean, JsonGenerator jgen, SerializerProvider prov)
}

@Override
public void serializeWithType(Object bean, JsonGenerator jgen, SerializerProvider provider,
public void serializeWithType(Object bean, JsonGenerator gen, SerializerProvider provider,
TypeSerializer typeSer0) throws IOException
{
// Regardless of other parts, first need to find value to serialize:
Expand All @@ -202,30 +204,29 @@ public void serializeWithType(Object bean, JsonGenerator jgen, SerializerProvide
value = _accessorMethod.getValue(bean);
// and if we got null, can also just write it directly
if (value == null) {
provider.defaultSerializeNull(jgen);
provider.defaultSerializeNull(gen);
return;
}
JsonSerializer<Object> ser = _valueSerializer;
if (ser == null) { // already got a serializer? fabulous, that be easy...
if (ser == null) { // no serializer yet? Need to fetch
// ser = provider.findTypedValueSerializer(value.getClass(), true, _property);
ser = provider.findValueSerializer(value.getClass(), _property);
} else {
/* 09-Dec-2010, tatu: To work around natural type's refusal to add type info, we do
* this (note: type is for the wrapper type, not enclosed value!)
*/
if (_forceTypeInformation) {
typeSer0.writeTypePrefixForScalar(bean, jgen);
ser.serialize(value, jgen, provider);
typeSer0.writeTypeSuffixForScalar(bean, jgen);
typeSer0.writeTypePrefixForScalar(bean, gen);
ser.serialize(value, gen, provider);
typeSer0.writeTypeSuffixForScalar(bean, gen);
return;
}
}
/* 13-Feb-2013, tatu: Turns out that work-around should NOT be required
* at all; it would not lead to correct behavior (as per #167).
*/
// and then redirect type id lookups
// TypeSerializer typeSer = new TypeSerializerWrapper(typeSer0, bean);
ser.serializeWithType(value, jgen, provider, typeSer0);
// 28-Sep-2016, tatu: As per [databind#1385], we do need to do some juggling
// to use different Object for type id (logical type) and actual serialization
// (delegat type).
TypeSerializerRerouter rr = new TypeSerializerRerouter(typeSer0, bean);
ser.serializeWithType(value, gen, provider, rr);
} catch (IOException ioe) {
throw ioe;
} catch (Exception e) {
Expand Down Expand Up @@ -339,7 +340,7 @@ protected boolean isNaturalTypeWithStdHandling(Class<?> rawType, JsonSerializer<
}
return isDefaultSerializer(ser);
}

/*
/**********************************************************
/* Other methods
Expand All @@ -350,4 +351,120 @@ protected boolean isNaturalTypeWithStdHandling(Class<?> rawType, JsonSerializer<
public String toString() {
return "(@JsonValue serializer for method " + _accessorMethod.getDeclaringClass() + "#" + _accessorMethod.getName() + ")";
}

/*
/**********************************************************
/* Helper class
/**********************************************************
*/

/**
* Silly little wrapper class we need to re-route type serialization so that we can
* override Object to use for type id (logical type) even when asking serialization
* of something else (delegate type)
*/
static class TypeSerializerRerouter
extends TypeSerializer
{
protected final TypeSerializer _typeSerializer;
protected final Object _forObject;

public TypeSerializerRerouter(TypeSerializer ts, Object ob) {
_typeSerializer = ts;
_forObject = ob;
}

@Override
public TypeSerializer forProperty(BeanProperty prop) { // should never get called
throw new UnsupportedOperationException();
}

@Override
public As getTypeInclusion() {
return _typeSerializer.getTypeInclusion();
}

@Override
public String getPropertyName() {
return _typeSerializer.getPropertyName();
}

@Override
public TypeIdResolver getTypeIdResolver() {
return _typeSerializer.getTypeIdResolver();
}

@Override
public void writeTypePrefixForScalar(Object value, JsonGenerator gen) throws IOException {
_typeSerializer.writeTypePrefixForScalar(_forObject, gen);
}

@Override
public void writeTypePrefixForObject(Object value, JsonGenerator gen) throws IOException {
_typeSerializer.writeTypePrefixForObject(_forObject, gen);
}

@Override
public void writeTypePrefixForArray(Object value, JsonGenerator gen) throws IOException {
_typeSerializer.writeTypePrefixForArray(_forObject, gen);
}

@Override
public void writeTypeSuffixForScalar(Object value, JsonGenerator gen) throws IOException {
_typeSerializer.writeTypeSuffixForScalar(_forObject, gen);
}

@Override
public void writeTypeSuffixForObject(Object value, JsonGenerator gen) throws IOException {
_typeSerializer.writeTypeSuffixForObject(_forObject, gen);
}

@Override
public void writeTypeSuffixForArray(Object value, JsonGenerator gen) throws IOException {
_typeSerializer.writeTypeSuffixForArray(_forObject, gen);
}

public void writeTypePrefixForScalar(Object value, JsonGenerator gen, Class<?> type) throws IOException {
_typeSerializer.writeTypePrefixForScalar(_forObject, gen, type);
}

public void writeTypePrefixForObject(Object value, JsonGenerator gen, Class<?> type) throws IOException {
_typeSerializer.writeTypePrefixForObject(_forObject, gen, type);
}

public void writeTypePrefixForArray(Object value, JsonGenerator gen, Class<?> type) throws IOException {
_typeSerializer.writeTypePrefixForArray(_forObject, gen, type);
}

@Override
public void writeCustomTypePrefixForScalar(Object value, JsonGenerator gen, String typeId)
throws IOException {
_typeSerializer.writeCustomTypePrefixForScalar(_forObject, gen, typeId);
}

@Override
public void writeCustomTypePrefixForObject(Object value, JsonGenerator gen, String typeId) throws IOException {
_typeSerializer.writeCustomTypePrefixForObject(_forObject, gen, typeId);
}

@Override
public void writeCustomTypePrefixForArray(Object value, JsonGenerator gen, String typeId) throws IOException {
_typeSerializer.writeCustomTypePrefixForArray(_forObject, gen, typeId);
}

@Override
public void writeCustomTypeSuffixForScalar(Object value, JsonGenerator gen, String typeId) throws IOException {
_typeSerializer.writeCustomTypeSuffixForScalar(_forObject, gen, typeId);
}

@Override
public void writeCustomTypeSuffixForObject(Object value, JsonGenerator gen, String typeId) throws IOException {
_typeSerializer.writeCustomTypeSuffixForObject(_forObject, gen, typeId);
}

@Override
public void writeCustomTypeSuffixForArray(Object value, JsonGenerator gen, String typeId) throws IOException {
_typeSerializer.writeCustomTypeSuffixForArray(_forObject, gen, typeId);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.fasterxml.jackson.databind.jsontype;

import org.junit.Assert;

import com.fasterxml.jackson.annotation.*;

import com.fasterxml.jackson.databind.BaseMapTest;
Expand All @@ -8,12 +10,6 @@
public class TestDefaultWithCreators
extends BaseMapTest
{
/*
/**********************************************************
/* Helper types
/**********************************************************
*/

static abstract class Job
{
public long id;
Expand All @@ -36,7 +32,31 @@ public UrlJob(@JsonProperty("id") long id, @JsonProperty("url") String url,
public String getUrl() { return url; }
public int getCount() { return count; }
}

// [databind#1385]
static class Bean1385Wrapper
{
public Object value;

protected Bean1385Wrapper() { }
public Bean1385Wrapper(Object v) { value = v; }
}

static class Bean1385
{
private byte[] raw;

@JsonCreator(mode=JsonCreator.Mode.DELEGATING)
public Bean1385(byte[] raw) {
this.raw = raw.clone();
}

@JsonValue
public byte[] getBytes() {
return raw;
}
}

/*
/**********************************************************
/* Unit tests
Expand All @@ -58,4 +78,21 @@ public void testWithCreators() throws Exception
assertEquals("http://foo", o2.getUrl());
assertEquals(3, o2.getCount());
}
}

// [databind#1385]
public void testWithCreatorAndJsonValue() throws Exception
{
final byte[] BYTES = new byte[] { 1, 2, 3, 4, 5 };
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();
String json = mapper.writeValueAsString(new Bean1385Wrapper(
new Bean1385(BYTES)
));
Bean1385Wrapper result = mapper.readValue(json, Bean1385Wrapper.class);
assertNotNull(result);
assertNotNull(result.value);
assertEquals(Bean1385.class, result.value.getClass());
Bean1385 b = (Bean1385) result.value;
Assert.assertArrayEquals(BYTES, b.raw);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -473,22 +473,14 @@ public void testWithAsValue() throws Exception
ExternalTypeWithNonPOJO input = new ExternalTypeWithNonPOJO(new AsValueThingy(12345L));
String json = MAPPER.writeValueAsString(input);
assertNotNull(json);
assertEquals("{\"value\":12345,\"type\":\"date\"}", json);
assertEquals("{\"value\":12345,\"type\":\"thingy\"}", json);

// and get it back too:
ExternalTypeWithNonPOJO result = MAPPER.readValue(json, ExternalTypeWithNonPOJO.class);
assertNotNull(result);
assertNotNull(result.value);
/* 13-Feb-2013, tatu: Urgh. I don't think this can work quite as intended...
* since POJO type, and type of thing @JsonValue annotated method returns
* are not related. Best we can do is thus this:
*/
/*
assertEquals(AsValueThingy.class, result.value.getClass());
assertEquals(12345L, ((AsValueThingy) result.value).rawDate);
*/
assertEquals(Date.class, result.value.getClass());
assertEquals(12345L, ((Date) result.value).getTime());
}

// for [databind#222]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,11 @@ static class External {
External(Internal e) { i = e.value; }
}

// [Issue#167]
// [databind#167]

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "boingo")
@JsonSubTypes(value = {@JsonSubTypes.Type(name = "boopsy", value = AdditionInterfaceImpl.class) })
@JsonSubTypes(value = {@JsonSubTypes.Type(name = "boopsy", value = AdditionInterfaceImpl.class)
})
static interface AdditionInterface
{
public int add(int in);
Expand All @@ -148,25 +149,6 @@ public int add(int in) {
return in + toAdd;
}
}

public static class NegatingAdditionInterface implements AdditionInterface
{
final AdditionInterface delegate;

public NegatingAdditionInterface(AdditionInterface delegate) {
this.delegate = delegate;
}

@Override
public int add(int in) {
return delegate.add(-in);
}

@JsonValue
public AdditionInterface getDelegate() {
return delegate;
}
}

static class Bean838 {
@JsonValue
Expand Down Expand Up @@ -258,13 +240,6 @@ public void testPolymorphicSerdeWithDelegate() throws Exception
String json = MAPPER.writeValueAsString(adder);
assertEquals("{\"boingo\":\"boopsy\",\"toAdd\":1}", json);
assertEquals(2, MAPPER.readValue(json, AdditionInterface.class).add(1));

adder = new NegatingAdditionInterface(adder);
assertEquals(0, adder.add(1));
json = MAPPER.writeValueAsString(adder);

assertEquals("{\"boingo\":\"boopsy\",\"toAdd\":1}", json);
assertEquals(2, MAPPER.readValue(json, AdditionInterface.class).add(1));
}

public void testJsonValueWithCustomOverride() throws Exception
Expand Down

0 comments on commit be114a9

Please sign in to comment.