Skip to content

Commit

Permalink
Fixed #18
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Oct 13, 2015
1 parent e87a739 commit 92223fe
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 41 deletions.
5 changes: 5 additions & 0 deletions release-notes/CREDITS
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ Jerome Loisel (jloisel@github)

* Reported #14: Missing `@type` when serializing Optional<Interface>
(2.6.2)

James Lorenzen (jlorenzen@github)

* Requested #18: Allow use of `@JsonInclude(content=Include.NON_EMPTY)` with `Optional<>`
(2.7.0)
3 changes: 3 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Project: jackson-datatype-jdk8

2.7.0 (not yet released)

#18: Allow use of `@JsonInclude(content=Include.NON_EMPTY)` with `Optional<>`
(requested by James L)

2.6.3 (not yet released)

#13: Allow use of `@JsonDeserialize(contentAs=XXX)` with `Optional`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import java.io.IOException;
import java.util.Optional;

import com.fasterxml.jackson.annotation.JsonInclude;

import com.fasterxml.jackson.core.JsonGenerator;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.introspect.Annotated;
Expand Down Expand Up @@ -35,14 +38,28 @@ public class OptionalSerializer
*/
protected final NameTransformer _unwrapper;

/**
* Further guidance on serialization-inclusion (or not), regarding
* contained value (if any).
*
* @since 2.7
*/
protected final JsonInclude.Include _contentInclusion;

/**
* If element type can not be statically determined, mapping from
* runtime type to serializer is handled using this object
*
* @since 2.6
*/
protected transient PropertySerializerMap _dynamicSerializers;


/*
/**********************************************************
/* Constructors, factory methods
/**********************************************************
*/

public OptionalSerializer(JavaType type) {
this(type, null);
}
Expand All @@ -55,30 +72,57 @@ protected OptionalSerializer(JavaType optionalType, JsonSerializer<?> valueSer)
_property = null;
_valueSerializer = (JsonSerializer<Object>) valueSer;
_unwrapper = null;
_contentInclusion = null;
_dynamicSerializers = PropertySerializerMap.emptyForProperties();
}

@SuppressWarnings("unchecked")
protected OptionalSerializer(OptionalSerializer base,
BeanProperty property, JsonSerializer<?> valueSer, NameTransformer unwrapper)
BeanProperty property, JsonSerializer<?> valueSer, NameTransformer unwrapper,
JsonInclude.Include contentIncl)
{
super(base);
_referredType = base._referredType;
_dynamicSerializers = base._dynamicSerializers;
_property = property;
_valueSerializer = (JsonSerializer<Object>) valueSer;
_unwrapper = unwrapper;
if ((contentIncl == JsonInclude.Include.USE_DEFAULTS)
|| (contentIncl == JsonInclude.Include.ALWAYS)) {
_contentInclusion = null;
} else {
_contentInclusion = contentIncl;
}
}

@Override
public JsonSerializer<Optional<?>> unwrappingSerializer(NameTransformer transformer) {
JsonSerializer<Object> ser = _valueSerializer;
if (ser != null) {
ser = ser.unwrappingSerializer(transformer);
}
NameTransformer unwrapper = (_unwrapper == null) ? transformer
: NameTransformer.chainedTransformer(transformer, _unwrapper);
return withResolved(_property, ser, unwrapper, _contentInclusion);
}

protected OptionalSerializer withResolved(BeanProperty prop,
JsonSerializer<?> ser, NameTransformer unwrapper)
JsonSerializer<?> ser, NameTransformer unwrapper,
JsonInclude.Include contentIncl)
{
if ((_property == prop) && (_valueSerializer == ser) && (_unwrapper == unwrapper)) {
if ((_property == prop) && (contentIncl == _contentInclusion)
&& (_valueSerializer == ser) && (_unwrapper == unwrapper)) {
return this;
}
return new OptionalSerializer(this, prop, ser, unwrapper);
return new OptionalSerializer(this, prop, ser, unwrapper, contentIncl);
}

/*
/**********************************************************
/* Contextualization (support for property annotations)
/**********************************************************
*/

@Override
public JsonSerializer<?> createContextual(SerializerProvider provider,
BeanProperty property) throws JsonMappingException
Expand All @@ -92,20 +136,38 @@ public JsonSerializer<?> createContextual(SerializerProvider provider,
} else {
ser = provider.handlePrimaryContextualization(ser, property);
}
return withResolved(property, ser, _unwrapper);
// Also: may want to have more refined exclusion based on referenced value
JsonInclude.Include contentIncl = _contentInclusion;
if (property != null) {
AnnotationIntrospector intr = provider.getAnnotationIntrospector();
if (intr != null) {
JsonInclude.Value incl = intr.findPropertyInclusion(property.getMember());
if (incl != null) {
JsonInclude.Include newIncl = incl.getContentInclusion();
if ((newIncl != contentIncl) && (newIncl != JsonInclude.Include.NON_DEFAULT)) {
contentIncl = newIncl;
}
}
}
}
return withResolved(property, ser, _unwrapper, contentIncl);
}

protected boolean _useStatic(SerializerProvider provider, BeanProperty property,
JavaType referredType)
{
// First: no serializer for `Object.class`, must be dynamic
if (_referredType.hasRawClass(Object.class)) {
if (_referredType.isJavaLangObject()) {
return false;
}
// but if type is final, might as well fetch
if (_referredType.isFinal()) { // or should we allow annotation override? (only if requested...)
return true;
}
// also: if indicated by typing, should be considered static
if (_referredType.useStaticType()) {
return true;
}
// if neither, maybe explicit annotation?
AnnotationIntrospector intr = provider.getAnnotationIntrospector();
if ((intr != null) && (property != null)) {
Expand All @@ -124,15 +186,36 @@ protected boolean _useStatic(SerializerProvider provider, BeanProperty property,
return provider.isEnabled(MapperFeature.USE_STATIC_TYPING);
}

/*
/**********************************************************
/* API overrides
/**********************************************************
*/

@Override
public JsonSerializer<Optional<?>> unwrappingSerializer(NameTransformer transformer) {
public boolean isEmpty(SerializerProvider provider, Optional<?> value)
{
if ((value == null) || !value.isPresent()) {
return true;
}
if (_contentInclusion == null) {
return false;
}
Object contents = value.get();
JsonSerializer<Object> ser = _valueSerializer;
if (ser != null) {
ser = ser.unwrappingSerializer(transformer);
if (ser == null) {
try {
ser = _findCachedSerializer(provider, value.getClass());
} catch (JsonMappingException e) { // nasty but necessary
throw new RuntimeJsonMappingException(e);
}
}
NameTransformer unwrapper = (_unwrapper == null) ? transformer
: NameTransformer.chainedTransformer(transformer, _unwrapper);
return withResolved(_property, ser, unwrapper);
return ser.isEmpty(provider, contents);
}

@Override
public boolean isUnwrappingSerializer() {
return (_unwrapper != null);
}

/*
Expand Down Expand Up @@ -174,22 +257,6 @@ public void serializeWithType(Optional<?> opt,
}
}

/*
/**********************************************************
/* API overrides
/**********************************************************
*/

@Override
public boolean isEmpty(SerializerProvider provider, Optional<?> value) {
return (value == null) || !value.isPresent();
}

@Override
public boolean isUnwrappingSerializer() {
return (_unwrapper != null);
}

/*
/**********************************************************
/* Introspection support
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.fasterxml.jackson.failing;
package com.fasterxml.jackson.datatype.jdk8;

import java.util.Optional;

Expand All @@ -11,7 +11,7 @@

import com.fasterxml.jackson.datatype.jdk8.ModuleTestBase;

public class OptionalBasicTest extends ModuleTestBase
public class OptionalnclusionTest extends ModuleTestBase
{
@JsonAutoDetect(fieldVisibility=Visibility.ANY)
public static final class OptionalData {
Expand All @@ -20,7 +20,7 @@ public static final class OptionalData {

// for [datatype-jdk8#18]
static class OptionalNonEmptyStringBean {
@JsonInclude(Include.NON_EMPTY)
@JsonInclude(value=Include.NON_EMPTY, content=Include.NON_EMPTY)
public Optional<String> value;

public OptionalNonEmptyStringBean() { }
Expand All @@ -31,15 +31,6 @@ public OptionalNonEmptyStringBean() { }

private final ObjectMapper MAPPER = mapperWithModule();

public void testSerOptNonDefault() throws Exception
{
OptionalData data = new OptionalData();
data.myString = null;
String value = mapperWithModule().setSerializationInclusion(
JsonInclude.Include.NON_DEFAULT).writeValueAsString(data);
assertEquals("{}", value);
}

public void testSerOptNonEmpty() throws Exception
{
OptionalData data = new OptionalData();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.fasterxml.jackson.failing;

import java.util.Optional;

import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;

import com.fasterxml.jackson.datatype.jdk8.ModuleTestBase;

public class OptionalnclusionTest extends ModuleTestBase
{
@JsonAutoDetect(fieldVisibility=Visibility.ANY)
public static final class OptionalData {
public Optional<String> myString = Optional.empty();
}

public void testSerOptNonDefault() throws Exception
{
OptionalData data = new OptionalData();
data.myString = null;
String value = mapperWithModule().setSerializationInclusion(
JsonInclude.Include.NON_DEFAULT).writeValueAsString(data);
assertEquals("{}", value);
}
}

0 comments on commit 92223fe

Please sign in to comment.