Skip to content

Commit

Permalink
Fix #756
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Apr 24, 2015
1 parent 6f60dd9 commit 914f643
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 13 deletions.
2 changes: 2 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Project: jackson-databind
#737: Add support for writing raw values in TokenBuffer
(suggested by Guillaume S, gsmet@github)
#743: Add `RawValue` helper type, for piping raw values through `TokenBuffer`
#756: Disabling SerializationFeature.FAIL_ON_EMPTY_BEANS does not affect `canSerialize()`
(reported by nickwongdev@github)
#762: Add `ObjectWriter.withoutRootName()`, `ObjectReader.withoutRootName()`
#765: `SimpleType.withStaticTyping()` impl incorrect
- Remove old cglib compatibility tests; cause problems in Eclipse
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ public abstract class SerializerProvider
new FailingSerializer("Null key for a Map not allowed in JSON (use a converting NullKeySerializer?)");

/**
* Placeholder serializer used when <code>java.lang.Object</code> typed property
* is marked to be serialized.
*<br />
* NOTE: starting with 2.6, this instance is NOT used for any other types, and
* separate instances are constructed for "empty" Beans.
*<p>
* NOTE: changed to <code>protected</code> for 2.3; no need to be publicly available.
*/
protected final static JsonSerializer<Object> DEFAULT_UNKNOWN_SERIALIZER = new UnknownSerializer();
Expand Down Expand Up @@ -816,7 +822,12 @@ public JsonSerializer<Object> findNullValueSerializer(BeanProperty property)
* @param unknownType Type for which no serializer is found
*/
public JsonSerializer<Object> getUnknownTypeSerializer(Class<?> unknownType) {
return _unknownTypeSerializer;
// 23-Apr-2015, tatu: Only return shared instance if nominal type is Object.class
if (unknownType == Object.class) {
return _unknownTypeSerializer;
}
// otherwise construct explicit instance with property handled type
return new UnknownSerializer(unknownType);
}

/**
Expand All @@ -827,7 +838,17 @@ public JsonSerializer<Object> getUnknownTypeSerializer(Class<?> unknownType) {
* @since 2.5
*/
public boolean isUnknownTypeSerializer(JsonSerializer<?> ser) {
return (ser == _unknownTypeSerializer) || (ser == null);
if ((ser == _unknownTypeSerializer) || (ser == null)) {
return true;
}
// 23-Apr-2015, tatu: "empty" serializer is trickier; needs to consider
// error handling
if (isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)) {
if (ser.getClass() == UnknownSerializer.class) {
return true;
}
}
return false;
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,42 @@ public class UnknownSerializer
public UnknownSerializer() {
super(Object.class);
}

/**
* @since 2.6
*/
public UnknownSerializer(Class<?> cls) {
super(cls, false);
}

@Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonMappingException
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException
{
// 27-Nov-2009, tatu: As per [JACKSON-201] may or may not fail...
if (provider.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)) {
failForEmpty(value);
}
// But if it's fine, we'll just output empty JSON Object:
jgen.writeStartObject();
jgen.writeEndObject();
gen.writeStartObject();
gen.writeEndObject();
}

@Override
public final void serializeWithType(Object value, JsonGenerator jgen, SerializerProvider provider,
TypeSerializer typeSer)
throws IOException, JsonGenerationException
public final void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider,
TypeSerializer typeSer) throws IOException
{
if (provider.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)) {
failForEmpty(value);
}
typeSer.writeTypePrefixForObject(value, jgen);
typeSer.writeTypeSuffixForObject(value, jgen);
typeSer.writeTypePrefixForObject(value, gen);
typeSer.writeTypeSuffixForObject(value, gen);
}


@Override
public boolean isEmpty(SerializerProvider provider, Object value) {
return true;
}

@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ protected StdSerializer(Class<?> t, boolean dummy) {
*/

@Override
public abstract void serialize(T value, JsonGenerator jgen, SerializerProvider provider)
public abstract void serialize(T value, JsonGenerator gen, SerializerProvider provider)
throws IOException, JsonGenerationException;

/*
Expand Down
15 changes: 15 additions & 0 deletions src/test/java/com/fasterxml/jackson/databind/ObjectMapperTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ static class Bean {
public void setX(int v) { value = v; }
}

static class EmptyBean { }

// for [Issue#206]
@SuppressWarnings("serial")
static class CustomMapper extends ObjectMapper {
Expand Down Expand Up @@ -200,4 +202,17 @@ public void testNonSerializabilityOfObject()
// but this used to pass, incorrectly
assertFalse(m.canSerialize(Object.class));
}

// for [databind#756]
public void testEmptyBeanSerializability()
{
// with default settings, error
/*
assertFalse(MAPPER.writer().with(SerializationFeature.FAIL_ON_EMPTY_BEANS)
.canSerialize(EmptyBean.class));
*/
// but with changes
assertTrue(MAPPER.writer().without(SerializationFeature.FAIL_ON_EMPTY_BEANS)
.canSerialize(EmptyBean.class));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
package com.fasterxml.jackson.databind;

import java.io.*;
import java.util.*;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.DefaultDeserializationContext;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.fasterxml.jackson.databind.node.*;
import com.fasterxml.jackson.databind.type.TypeFactory;

public class ObjectMapperTest extends BaseMapTest
{
static class Bean {
int value = 3;

public void setX(int v) { value = v; }
}

// for [Issue#206]
@SuppressWarnings("serial")
static class CustomMapper extends ObjectMapper {
@Override
protected DefaultDeserializationContext createDeserializationContext(JsonParser jp,
DeserializationConfig cfg) {
return super.createDeserializationContext(jp, cfg);
}
}

@SuppressWarnings("serial")
static class MyAnnotationIntrospector extends JacksonAnnotationIntrospector { }

/*
/**********************************************************
/* Test methods
/**********************************************************
*/

final static ObjectMapper MAPPER = new ObjectMapper();

public void testProps()
{
ObjectMapper m = new ObjectMapper();
// should have default factory
assertNotNull(m.getNodeFactory());
JsonNodeFactory nf = JsonNodeFactory.instance;
m.setNodeFactory(nf);
assertNull(m.getInjectableValues());
assertSame(nf, m.getNodeFactory());
}

public void testSupport()
{
assertTrue(MAPPER.canSerialize(String.class));
assertTrue(MAPPER.canDeserialize(TypeFactory.defaultInstance().constructType(String.class)));
}

public void testTreeRead() throws Exception
{
String JSON = "{ }";
JsonNode n = MAPPER.readTree(JSON);
assertTrue(n instanceof ObjectNode);

n = MAPPER.readTree(new StringReader(JSON));
assertTrue(n instanceof ObjectNode);

n = MAPPER.readTree(new ByteArrayInputStream(JSON.getBytes("UTF-8")));
assertTrue(n instanceof ObjectNode);
}

// Test to ensure that we can check property ordering defaults...
public void testConfigForPropertySorting() throws Exception
{
ObjectMapper m = new ObjectMapper();

// sort-alphabetically is disabled by default:
assertFalse(m.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY));
SerializationConfig sc = m.getSerializationConfig();
assertFalse(sc.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY));
assertFalse(sc.shouldSortPropertiesAlphabetically());
DeserializationConfig dc = m.getDeserializationConfig();
assertFalse(dc.shouldSortPropertiesAlphabetically());

// but when enabled, should be visible:
m.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
sc = m.getSerializationConfig();
assertTrue(sc.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY));
assertTrue(sc.shouldSortPropertiesAlphabetically());
dc = m.getDeserializationConfig();
// and not just via SerializationConfig, but also via DeserializationConfig
assertTrue(dc.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY));
assertTrue(dc.shouldSortPropertiesAlphabetically());
}


public void testJsonFactoryLinkage()
{
// first, implicit factory, giving implicit linkage
assertSame(MAPPER, MAPPER.getFactory().getCodec());

// and then explicit factory, which should also be implicitly linked
JsonFactory f = new JsonFactory();
ObjectMapper m = new ObjectMapper(f);
assertSame(f, m.getFactory());
assertSame(m, f.getCodec());
}

/**
* Test for verifying working of [JACKSON-191]
*/
public void testProviderConfig() throws Exception
{
ObjectMapper m = new ObjectMapper();
final String JSON = "{ \"x\" : 3 }";

assertEquals(0, m._deserializationContext._cache.cachedDeserializersCount());
// and then should get one constructed for:
Bean bean = m.readValue(JSON, Bean.class);
assertNotNull(bean);
// Since 2.6, serializer for int also cached:
assertEquals(2, m._deserializationContext._cache.cachedDeserializersCount());
m._deserializationContext._cache.flushCachedDeserializers();
assertEquals(0, m._deserializationContext._cache.cachedDeserializersCount());

// 07-Nov-2014, tatu: As per [databind#604] verify that Maps also get cached
m = new ObjectMapper();
List<?> stuff = m.readValue("[ ]", List.class);
assertNotNull(stuff);
// may look odd, but due to "Untyped" deserializer thing, we actually have
// 4 deserializers (int, List<?>, Map<?,?>, Object)
assertEquals(4, m._deserializationContext._cache.cachedDeserializersCount());
}

// [Issue#28]: ObjectMapper.copy()
public void testCopy() throws Exception
{
ObjectMapper m = new ObjectMapper();
assertTrue(m.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
m.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
assertFalse(m.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
InjectableValues inj = new InjectableValues.Std();
m.setInjectableValues(inj);

// // First: verify that handling of features is decoupled:

ObjectMapper m2 = m.copy();
assertFalse(m2.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
m2.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
assertTrue(m2.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
assertSame(inj, m2.getInjectableValues());

// but should NOT change the original
assertFalse(m.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));

// nor vice versa:
assertFalse(m.isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE));
assertFalse(m2.isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE));
m.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
assertTrue(m.isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE));
assertFalse(m2.isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE));

// // Also, underlying JsonFactory instances should be distinct

assertNotSame(m.getFactory(), m2.getFactory());

// [Issue#122]: Need to ensure mix-ins are not shared
assertEquals(0, m.getSerializationConfig().mixInCount());
assertEquals(0, m2.getSerializationConfig().mixInCount());
assertEquals(0, m.getDeserializationConfig().mixInCount());
assertEquals(0, m2.getDeserializationConfig().mixInCount());

m.addMixIn(String.class, Integer.class);
assertEquals(1, m.getSerializationConfig().mixInCount());
assertEquals(0, m2.getSerializationConfig().mixInCount());
assertEquals(1, m.getDeserializationConfig().mixInCount());
assertEquals(0, m2.getDeserializationConfig().mixInCount());
}

public void testAnnotationIntrospectorCopyin()
{
ObjectMapper m = new ObjectMapper();
m.setAnnotationIntrospector(new MyAnnotationIntrospector());
assertEquals(MyAnnotationIntrospector.class,
m.getDeserializationConfig().getAnnotationIntrospector().getClass());
ObjectMapper m2 = m.copy();

assertEquals(MyAnnotationIntrospector.class,
m2.getDeserializationConfig().getAnnotationIntrospector().getClass());
assertEquals(MyAnnotationIntrospector.class,
m2.getSerializationConfig().getAnnotationIntrospector().getClass());
}

// For [databind#703]
public void testNonSerializabilityOfObject()
{
ObjectMapper m = new ObjectMapper();
assertFalse(m.canSerialize(Object.class));
// but this used to pass, incorrectly
assertFalse(m.canSerialize(Object.class));
}
}

0 comments on commit 914f643

Please sign in to comment.