Skip to content

Commit

Permalink
Fix #2458 (Nulls metadata not used for creator parameters)
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Sep 23, 2019
1 parent 971a3b6 commit d07238a
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 13 deletions.
2 changes: 2 additions & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Project: jackson-databind
#2077: `JsonTypeInfo` with a subtype having `JsonFormat.Shape.ARRAY` and
no fields generates `{}` not `[]`
(reported by Sadayuki F)
#2458: `Nulls` property metadata ignored for creators
(reported by XakepSDK@github)
#2466: Didn't find class "java.nio.file.Path" below Android api 26
(reported by KevynBct@github)
#2467: Accept `JsonTypeInfo.As.WRAPPER_ARRAY` with no second argument to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@
import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.Nulls;
import com.fasterxml.jackson.annotation.JsonCreator.Mode;

import com.fasterxml.jackson.core.JsonParser;

import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.ConfigOverride;
import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
import com.fasterxml.jackson.databind.deser.impl.CreatorCandidate;
Expand Down Expand Up @@ -1003,11 +1006,14 @@ protected SettableBeanProperty constructCreatorProperty(DeserializationContext c
if (typeDeser == null) {
typeDeser = findTypeDeserializer(config, type);
}

// 22-Sep-2019, tatu: for [databind#2458] need more work on getting metadata
// about SetterInfo, mergeability
metadata = _getSetterInfo(ctxt, property, metadata);

// Note: contextualization of typeDeser _should_ occur in constructor of CreatorProperty
// so it is not called directly here

Object injectableValueId = (injectable == null) ? null : injectable.getId();

SettableBeanProperty prop = new CreatorProperty(name, type, property.getWrapperName(),
typeDeser, beanDesc.getClassAnnotations(), param, index, injectableValueId,
metadata);
Expand Down Expand Up @@ -1041,6 +1047,65 @@ private PropertyName _findParamName(AnnotatedParameter param, AnnotationIntrospe
return null;
}

/**
* Helper method copied from {@code POJOPropertyBuilder} since that won't be
* applied to creator parameters
*
* @since 2.10
*/
protected PropertyMetadata _getSetterInfo(DeserializationContext ctxt,
BeanProperty prop, PropertyMetadata metadata)
{
final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
final DeserializationConfig config = ctxt.getConfig();

boolean needMerge = true;
Nulls valueNulls = null;
Nulls contentNulls = null;

// NOTE: compared to `POJOPropertyBuilder`, we only have access to creator
// parameter, not other accessors, so code bit simpler
AnnotatedMember prim = prop.getMember();

if (prim != null) {
// Ok, first: does property itself have something to say?
if (intr != null) {
JsonSetter.Value setterInfo = intr.findSetterInfo(prim);
if (setterInfo != null) {
valueNulls = setterInfo.nonDefaultValueNulls();
contentNulls = setterInfo.nonDefaultContentNulls();
}
}
// If not, config override?
// 25-Oct-2016, tatu: Either this, or type of accessor...
if (needMerge || (valueNulls == null) || (contentNulls == null)) {
ConfigOverride co = config.getConfigOverride(prop.getType().getRawClass());
JsonSetter.Value setterInfo = co.getSetterInfo();
if (setterInfo != null) {
if (valueNulls == null) {
valueNulls = setterInfo.nonDefaultValueNulls();
}
if (contentNulls == null) {
contentNulls = setterInfo.nonDefaultContentNulls();
}
}
}
}
if (needMerge || (valueNulls == null) || (contentNulls == null)) {
JsonSetter.Value setterInfo = config.getDefaultSetterInfo();
if (valueNulls == null) {
valueNulls = setterInfo.nonDefaultValueNulls();
}
if (contentNulls == null) {
contentNulls = setterInfo.nonDefaultContentNulls();
}
}
if ((valueNulls != null) || (contentNulls != null)) {
metadata = metadata.withNulls(valueNulls, contentNulls);
}
return metadata;
}

/*
/**********************************************************
/* JsonDeserializerFactory impl: array deserializers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,13 @@ protected Object _findMissing(SettableBeanProperty prop) throws JsonMappingExcep
"Missing creator property '%s' (index %d); `DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES` enabled",
prop.getName(), prop.getCreatorIndex());
}
// Third: default value
// Third: NullValueProvider? (22-Sep-2019, [databind#2458])
Object nullValue = prop.getNullValueProvider().getNullValue(_context);
if (nullValue != null) {
return nullValue;
}

// Fourth: default value
JsonDeserializer<Object> deser = prop.getValueDeserializer();
return deser.getNullValue(_context);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,42 @@

public class NullConversions2458Test extends BaseMapTest
{
// [databind#2458]
static class Pojo {
List<String> list;
List<String> _value;

@JsonCreator
public Pojo(@JsonProperty("list") List<String> list) {
this.list = Objects.requireNonNull(list, "list");
public Pojo(@JsonProperty("value") List<String> v) {
this._value = Objects.requireNonNull(v, "value");
}

public List<String> getList() {
return list;
protected Pojo() { }

public List<String> value() {
return _value;
}

public void setOther(List<String> v) { }
}

private final ObjectMapper MAPPER_WITH_AS_EMPTY = jsonMapperBuilder()
.defaultSetterInfo(JsonSetter.Value.construct(Nulls.AS_EMPTY,
Nulls.AS_EMPTY))
.build();

// [databind#2458]
public void testMissingToEmptyViaCreator() throws Exception {
Pojo pojo = MAPPER_WITH_AS_EMPTY.readValue("{}", Pojo.class);
assertNotNull(pojo);
assertNotNull(pojo.value());
assertEquals(0, pojo.value().size());
}

public void testNullsViaCreator() throws Exception {
ObjectMapper mapper = newJsonMapper();
mapper.setDefaultSetterInfo(JsonSetter.Value.construct(Nulls.AS_EMPTY,
Nulls.AS_EMPTY));
Pojo pojo = mapper.readValue("{}", Pojo.class);
// [databind#2458]
public void testNullToEmptyViaCreator() throws Exception {
Pojo pojo = MAPPER_WITH_AS_EMPTY.readValue("{\"value\":null}", Pojo.class);
assertNotNull(pojo);
assertNotNull(pojo.value());
assertEquals(0, pojo.value().size());
}
}

0 comments on commit d07238a

Please sign in to comment.