Skip to content

Commit

Permalink
Fix #507
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Oct 11, 2016
1 parent 7ce734a commit f97a53f
Show file tree
Hide file tree
Showing 13 changed files with 177 additions and 74 deletions.
2 changes: 2 additions & 0 deletions release-notes/CREDITS
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,8 @@ Mark Woon (markwoon@github)
(2.7.4)
* Reported #1231: `@JsonSerialize(as=superType)` behavior disallowed in 2.7.4
(2.7.5)
* Suggested #507: Support for default `@JsonView` for a class
(2.9.0)

Tom Mack (tommack@github)
* Reported #1208: treeToValue doesn't handle POJONodes that contain exactly
Expand Down
2 changes: 2 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Project: jackson-databind

2.9.0 (not yet released)

#507: Support for default `@JsonView` for a class
(suggested by Mark W)
#888: Allow specifying custom exclusion comparator via `@JsonInclude`,
using `JsonInclude.Include.CUSTOM`
#1035: `@JsonAnySetter` assumes key of `String`, does not consider declared type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,4 +272,13 @@ protected BeanDescription(JavaType type) {
* suitable default constructor was found; null otherwise.
*/
public abstract Object instantiateBean(boolean fixAccess);

/**
* Method for finding out if the POJO specifies default view(s) to
* use for properties, considering both per-type annotations and
* global default settings.
*
* @since 2.9
*/
public abstract Class<?>[] findDefaultViews();
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ public class BeanDeserializerFactory
*/
private final static Class<?>[] INIT_CAUSE_PARAMS = new Class<?>[] { Throwable.class };

private final static Class<?>[] NO_VIEWS = new Class<?>[0];

/*
/**********************************************************
/* Life-cycle
Expand Down Expand Up @@ -444,7 +442,7 @@ protected void addBeanProps(DeserializationContext ctxt,
final SettableBeanProperty[] creatorProps =
builder.getValueInstantiator().getFromObjectArguments(ctxt.getConfig());
final boolean isConcrete = !beanDesc.getType().isAbstract();

// 01-May-2016, tatu: Which base type to use here gets tricky, since
// it may often make most sense to use general type for overrides,
// but what we have here may be more specific impl type. But for now
Expand All @@ -471,12 +469,11 @@ protected void addBeanProps(DeserializationContext ctxt,
AnnotatedMember anySetterField = null;
if (anySetterMethod != null) {
builder.setAnySetter(constructAnySetter(ctxt, beanDesc, anySetterMethod));
}
else {
anySetterField = beanDesc.findAnySetterField();
if(anySetterField != null) {
builder.setAnySetter(constructAnySetter(ctxt, beanDesc, anySetterField));
}
} else {
anySetterField = beanDesc.findAnySetterField();
if (anySetterField != null) {
builder.setAnySetter(constructAnySetter(ctxt, beanDesc, anySetterField));
}
}
// NOTE: we do NOT add @JsonIgnore'd properties into blocked ones if there's any-setter
// Implicit ones via @JsonIgnore and equivalent?
Expand Down Expand Up @@ -562,20 +559,17 @@ protected void addBeanProps(DeserializationContext ctxt,
}

if (prop != null) {
// one more thing before adding to builder: copy any metadata
Class<?>[] views = propDef.findViews();
if (views == null) {
// one more twist: if default inclusion disabled, need to force empty set of views
if (!ctxt.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION)) {
views = NO_VIEWS;
}
views = beanDesc.findDefaultViews();
}
// one more thing before adding to builder: copy any metadata
prop.setViews(views);
builder.addProperty(prop);
}
}
}

/**
* Helper method called to filter out explicit ignored properties,
* as well as properties that have "ignorable types".
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
*/
public class BasicBeanDescription extends BeanDescription
{
// since 2.9
private final static Class<?>[] NO_VIEWS = new Class<?>[0];

/*
/**********************************************************
/* General configuration
Expand All @@ -43,12 +46,28 @@ public class BasicBeanDescription extends BeanDescription
final protected MapperConfig<?> _config;

final protected AnnotationIntrospector _annotationIntrospector;

/*
/**********************************************************
/* Information about type itself
/**********************************************************
*/

/**
* Information collected about the class introspected.
*/
final protected AnnotatedClass _classInfo;

/**
* @since 2.9
*/
protected Class<?>[] _defaultViews;

/**
* @since 2.9
*/
protected boolean _defaultViewsResolved;

/*
/**********************************************************
/* Member information
Expand Down Expand Up @@ -365,7 +384,25 @@ public JsonFormat.Value findExpectedFormat(JsonFormat.Value defValue)
}
return defValue;
}


@Override // since 2.9
public Class<?>[] findDefaultViews()
{
if (!_defaultViewsResolved) {
_defaultViewsResolved = true;
Class<?>[] def = (_annotationIntrospector == null) ? null
: _annotationIntrospector.findViews(_classInfo);
// one more twist: if default inclusion disabled, need to force empty set of views
if (def == null) {
if (!_config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION)) {
def = NO_VIEWS;
}
}
_defaultViews = def;
}
return _defaultViews;
}

/*
/**********************************************************
/* Introspection for serialization
Expand Down Expand Up @@ -664,7 +701,7 @@ public LinkedHashMap<String,AnnotatedField> _findPropertyFields(
*/

@SuppressWarnings("unchecked")
public Converter<Object,Object> _createConverter(Object converterDef)
protected Converter<Object,Object> _createConverter(Object converterDef)
{
if (converterDef == null) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,19 +201,23 @@ public class BeanPropertyWriter extends PropertyWriter // which extends
/***********************************************************
*/

/**
* @since 2.9 (added `includeInViews` since 2.8)
*/
@SuppressWarnings("unchecked")
public BeanPropertyWriter(BeanPropertyDefinition propDef,
AnnotatedMember member, Annotations contextAnnotations,
JavaType declaredType, JsonSerializer<?> ser,
TypeSerializer typeSer, JavaType serType, boolean suppressNulls,
Object suppressableValue) {
JavaType declaredType,
JsonSerializer<?> ser, TypeSerializer typeSer, JavaType serType,
boolean suppressNulls, Object suppressableValue,
Class<?>[] includeInViews)
{
super(propDef);
_member = member;
_contextAnnotations = contextAnnotations;

_name = new SerializedString(propDef.getName());
_wrapperName = propDef.getWrapperName();
_includeInViews = propDef.findViews();

_declaredType = declaredType;
_serializer = (JsonSerializer<Object>) ser;
Expand All @@ -239,6 +243,19 @@ public BeanPropertyWriter(BeanPropertyDefinition propDef,

// this will be resolved later on, unless nulls are to be suppressed
_nullSerializer = null;
_includeInViews = includeInViews;
}

@Deprecated // Since 2.9
public BeanPropertyWriter(BeanPropertyDefinition propDef,
AnnotatedMember member, Annotations contextAnnotations,
JavaType declaredType,
JsonSerializer<?> ser, TypeSerializer typeSer, JavaType serType,
boolean suppressNulls, Object suppressableValue)
{
this(propDef, member, contextAnnotations, declaredType,
ser, typeSer, serType, suppressNulls, suppressableValue,
null);
}

/**
Expand Down Expand Up @@ -605,6 +622,7 @@ public JavaType getSerializationType() {
return _cfgSerializationType;
}

@Deprecated // since 2.9
public Class<?> getRawSerializationType() {
return (_cfgSerializationType == null) ? null : _cfgSerializationType
.getRawClass();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public class PropertyBuilder
* @since 2.8
*/
final protected boolean _useRealPropertyDefaults;

public PropertyBuilder(SerializationConfig config, BeanDescription beanDesc)
{
_config = config;
Expand Down Expand Up @@ -86,7 +86,6 @@ public Annotations getClassAnnotations() {
* to use for contained values (only used for properties that are
* of container type)
*/
@SuppressWarnings("deprecation")
protected BeanPropertyWriter buildWriter(SerializerProvider prov,
BeanPropertyDefinition propDef, JavaType declaredType, JsonSerializer<?> ser,
TypeSerializer typeSer, TypeSerializer contentTypeSer,
Expand Down Expand Up @@ -202,9 +201,13 @@ protected BeanPropertyWriter buildWriter(SerializerProvider prov,
}
break;
}
Class<?>[] views = propDef.findViews();
if (views == null) {
views = _beanDesc.findDefaultViews();
}
BeanPropertyWriter bpw = new BeanPropertyWriter(propDef,
am, _beanDesc.getClassAnnotations(), declaredType,
ser, typeSer, serializationType, suppressNulls, valueToSuppress);
ser, typeSer, serializationType, suppressNulls, valueToSuppress, views);

// How about custom null serializer?
Object serDef = _annotationIntrospector.findNullSerializer(am);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,23 @@ protected VirtualBeanPropertyWriter() {
protected VirtualBeanPropertyWriter(BeanPropertyDefinition propDef,
Annotations contextAnnotations, JavaType declaredType,
JsonSerializer<?> ser, TypeSerializer typeSer, JavaType serType,
JsonInclude.Value inclusion)
JsonInclude.Value inclusion, Class<?>[] includeInViews)
{
super(propDef, propDef.getPrimaryMember(), contextAnnotations, declaredType,
ser, typeSer, serType,
_suppressNulls(inclusion), _suppressableValue(inclusion));
_suppressNulls(inclusion), _suppressableValue(inclusion),
includeInViews);
}

@Deprecated // since 2.8
protected VirtualBeanPropertyWriter(BeanPropertyDefinition propDef,
Annotations contextAnnotations, JavaType declaredType,
JsonSerializer<?> ser, TypeSerializer typeSer, JavaType serType,
JsonInclude.Value inclusion)
{
this(propDef, contextAnnotations, declaredType, ser, typeSer, serType, inclusion, null);
}

protected VirtualBeanPropertyWriter(VirtualBeanPropertyWriter base) {
super(base);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ protected AttributePropertyWriter(String attrName, BeanPropertyDefinition propDe
{
super(propDef, contextAnnotations, declaredType,
/* value serializer */ null, /* type serializer */ null, /* ser type */ null,
inclusion);
inclusion,
// 10-Oct-2016, tatu: Could enable per-view settings too in future
null);
_attrName = attrName;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,9 +318,6 @@ public void resolve(SerializerProvider provider)
// It not, we can use declared return type if and only if declared type is final:
// if not, we don't really know the actual type until we get the instance.
if (type == null) {
// 30-Oct-2015, tatu: Not sure why this was used
// type = provider.constructType(prop.getGenericPropertyType());
// but this looks better
type = prop.getType();
if (!type.isFinal()) {
if (type.isContainerType() || type.containedTypeCount() > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,8 @@ public List<BeanPropertyWriter> changeProperties(SerializationConfig config,
beanProperties.add(new BeanPropertyWriter(prop, f, null,
strType,
null, null, strType,
false, null));
false, null,
null));
} catch (NoSuchFieldException e) {
throw new IllegalStateException(e.getMessage());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.fasterxml.jackson.databind.views;

import java.io.IOException;

import com.fasterxml.jackson.annotation.*;

import com.fasterxml.jackson.databind.*;

// for [databind#507], supporting default views
public class DefaultViewTest extends BaseMapTest
{
// Classes that represent views
static class ViewA { }
static class ViewAA extends ViewA { }
static class ViewB { }
static class ViewBB extends ViewB { }

@JsonView(ViewA.class)
@JsonPropertyOrder({ "a", "b" })
static class Defaulting {
public int a = 3;

@JsonView(ViewB.class)
public int b = 5;
}

/*
/**********************************************************
/* Unit tests
/**********************************************************
*/

private final ObjectMapper MAPPER = new ObjectMapper();

public void testDeserialization() throws IOException
{
final String JSON = aposToQuotes("{'a':1,'b':2}");

// first: no views:
Defaulting result = MAPPER.readerFor(Defaulting.class)
.readValue(JSON);
assertEquals(result.a, 1);
assertEquals(result.b, 2);

// Then views; first A, then B(B)
result = MAPPER.readerFor(Defaulting.class)
.withView(ViewA.class)
.readValue(JSON);
assertEquals(result.a, 1);
assertEquals(result.b, 5);

result = MAPPER.readerFor(Defaulting.class)
.withView(ViewBB.class)
.readValue(JSON);
assertEquals(result.a, 3);
assertEquals(result.b, 2);
}

public void testSerialization() throws IOException
{
assertEquals(aposToQuotes("{'a':3,'b':5}"),
MAPPER.writeValueAsString(new Defaulting()));

assertEquals(aposToQuotes("{'a':3}"),
MAPPER.writerWithView(ViewA.class)
.writeValueAsString(new Defaulting()));
assertEquals(aposToQuotes("{'b':5}"),
MAPPER.writerWithView(ViewB.class)
.writeValueAsString(new Defaulting()));
}
}
Loading

0 comments on commit f97a53f

Please sign in to comment.