Skip to content

Commit

Permalink
Last tweaks wrt #614
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Nov 17, 2014
1 parent a56270a commit ef4115c
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,30 +99,30 @@ protected BeanDescription(JavaType type) {
/* Basic API for finding properties
/**********************************************************
*/

/**
* @return Ordered Map with logical property name as key, and
* matching getter method as value.
*/
public abstract List<BeanPropertyDefinition> findProperties();

/**
* Method for locating all back-reference properties (setters, fields) bean has
*/
public abstract Map<String,AnnotatedMember> findBackReferenceProperties();

public abstract Set<String> getIgnoredPropertyNames();

/*
/**********************************************************
/* Basic API for finding creator members
/**********************************************************
*/

public abstract List<AnnotatedConstructor> getConstructors();

public abstract List<AnnotatedMethod> getFactoryMethods();

/**
* Method that will locate the no-arg constructor for this class,
* if it has one, and that constructor has not been marked as
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -437,34 +437,24 @@ protected void _addDeserializerConstructors(DeserializationContext ctxt, BeanDes

// some single-arg factory methods (String, number) are auto-detected
if (argCount == 1) {
BeanPropertyDefinition propDef = (propDefs == null) ? null : propDefs[0];
boolean hasExplicitName = (propDef != null) && propDef.isExplicitlyNamed();
Object injectId = intr.findInjectableValueId(ctor.getParameter(0));
BeanPropertyDefinition argDef = (propDefs == null) ? null : propDefs[0];
boolean useProps = _checkIfCreatorPropertyBased(intr, ctor, argDef);

JsonCreator.Mode mode = intr.findCreatorBinding(ctor);

boolean withProps = (mode == JsonCreator.Mode.PROPERTIES);
if (!withProps && (mode != JsonCreator.Mode.DELEGATING)) {
if (hasExplicitName || (injectId != null)) {
withProps = true;
} else {
// TODO: one more thing -- if property has matching field
// or setter, also consider property-based
}
}
if (withProps) {
if (useProps) {
CreatorProperty[] properties = new CreatorProperty[1];
PropertyName name = (propDef == null) ? null : propDef.getFullName();
properties[0] = constructCreatorProperty(ctxt, beanDesc, name, 0, ctor.getParameter(0), injectId);
PropertyName name = (argDef == null) ? null : argDef.getFullName();
AnnotatedParameter arg = ctor.getParameter(0);
properties[0] = constructCreatorProperty(ctxt, beanDesc, name, 0, arg,
intr.findInjectableValueId(arg));
creators.addPropertyCreator(ctor, properties);
} else {
/*boolean added = */ _handleSingleArgumentConstructor(ctxt, beanDesc, vchecker, intr, creators,
ctor, isCreator,
vchecker.isCreatorVisible(ctor));
// one more thing: sever link to creator property, to avoid possible later
// problems with "unresolved" constructor property
if (propDef != null) {
((POJOPropertyBuilder) propDef).removeConstructors();
if (argDef != null) {
((POJOPropertyBuilder) argDef).removeConstructors();
}
}
// regardless, fully handled
Expand Down Expand Up @@ -528,6 +518,36 @@ protected void _addDeserializerConstructors(DeserializationContext ctxt, BeanDes
}
}

protected boolean _checkIfCreatorPropertyBased(AnnotationIntrospector intr,
AnnotatedWithParams creator, BeanPropertyDefinition propDef)
{
JsonCreator.Mode mode = intr.findCreatorBinding(creator);

if (mode == JsonCreator.Mode.PROPERTIES) {
return true;
}
if (mode == JsonCreator.Mode.DELEGATING) {
return false;
}
// If explicit name, or inject id, property-based
if (((propDef != null) && propDef.isExplicitlyNamed())
|| (intr.findInjectableValueId(creator.getParameter(0)) != null)) {
return true;
}
if (propDef != null) {
// One more thing: if implicit name matches property with a getter
// or field, we'll consider it property-based as well
String implName = propDef.getName();
if (implName != null && !implName.isEmpty()) {
if (propDef.couldSerialize()) {
return true;
}
}
}
// in absence of everything else, default to delegating
return false;
}

protected boolean _handleSingleArgumentConstructor(DeserializationContext ctxt,
BeanDescription beanDesc, VisibilityChecker<?> vchecker,
AnnotationIntrospector intr, CreatorCollector creators,
Expand Down Expand Up @@ -604,22 +624,9 @@ protected void _addDeserializerFactoryMethods(DeserializationContext ctxt, BeanD
final BeanPropertyDefinition[] propDefs = creatorParams.get(factory);
// some single-arg factory methods (String, number) are auto-detected
if (argCount == 1) {
BeanPropertyDefinition propDef = (propDefs == null) ? null : propDefs[0];
boolean hasExplicitName = (propDef != null) && propDef.isExplicitlyNamed();
final Object injectId = intr.findInjectableValueId(factory.getParameter(0));

JsonCreator.Mode mode = intr.findCreatorBinding(factory);

boolean withProps = (mode == JsonCreator.Mode.PROPERTIES);
if (!withProps && (mode != JsonCreator.Mode.DELEGATING)) {
if (hasExplicitName || (injectId != null)) {
withProps = true;
} else {
// TODO: one more thing -- if property has matching field
// or setter, also consider property-based
}
}
if (!withProps) { // not property based but delegating
BeanPropertyDefinition argDef = (propDefs == null) ? null : propDefs[0];
boolean useProps = _checkIfCreatorPropertyBased(intr, factory, argDef);
if (!useProps) { // not property based but delegating
/*boolean added=*/ _handleSingleArgumentFactory(config, beanDesc, vchecker, intr, creators,
factory, isCreator);
// otherwise just ignored
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1068,7 +1068,7 @@ protected Object deserializeFromObjectUsingNonDefault(JsonParser jp,
+" (need to add/enable type information?)");
}
throw JsonMappingException.from(jp, "No suitable constructor found for type "
+_beanType+": can not instantiate from JSON object (need to add/enable type information?)");
+_beanType+": can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)");
}

protected abstract Object _deserializeUsingPropertyBased(final JsonParser jp,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ public boolean removeProperty(String propName)

@Override
public ObjectIdInfo getObjectIdInfo() { return _objectIdInfo; }

@Override
public List<BeanPropertyDefinition> findProperties() {
return _properties;
Expand All @@ -193,7 +193,7 @@ public Set<String> getIgnoredPropertyNames() {
}
return _ignoredPropertyNames;
}

@Override
public boolean hasKnownClassAnnotations() {
return _classInfo.hasAnnotations();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,31 @@ public String getValue() {
}
}

static class StringyBeanWithProps
{
public final String value;

@JsonCreator
private StringyBeanWithProps(String v) { value = v; }

public String getValue() {
return value;
}
}

@SuppressWarnings("serial")
static class MyParamIntrospector extends JacksonAnnotationIntrospector
{
private final String name;

public MyParamIntrospector(String n) { name = n; }

@Override
public String findImplicitPropertyName(AnnotatedMember param) {
if (param instanceof AnnotatedParameter) {
AnnotatedParameter ap = (AnnotatedParameter) param;
switch (ap.getIndex()) {
case 0: return "value";
case 0: return name;
}
return "param"+ap.getIndex();
}
Expand All @@ -80,11 +96,20 @@ public void testNamedSingleArg() throws Exception
public void testSingleStringArgWithImplicitName() throws Exception
{
final ObjectMapper mapper = new ObjectMapper();
mapper.setAnnotationIntrospector(new MyParamIntrospector());
mapper.setAnnotationIntrospector(new MyParamIntrospector("value"));
StringyBean bean = mapper.readValue(quote("foobar"), StringyBean.class);
assertEquals("foobar", bean.getValue());
}

// [databind#714]
public void testSingleImplicitlyNamedNotDelegating() throws Exception
{
final ObjectMapper mapper = new ObjectMapper();
mapper.setAnnotationIntrospector(new MyParamIntrospector("value"));
StringyBeanWithProps bean = mapper.readValue("{\"value\":\"x\"}", StringyBeanWithProps.class);
assertEquals("x", bean.getValue());
}

// [databind#714]
public void testSingleExplicitlyNamedButDelegating() throws Exception
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
package com.fasterxml.jackson.databind.deser;

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.core.JsonParseException;
import java.io.IOException;

import com.fasterxml.jackson.annotation.*;

import com.fasterxml.jackson.databind.BaseMapTest;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
* Test for testing forward reference handling
*/
Expand Down Expand Up @@ -67,7 +60,7 @@ public void setId(String id) {
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
private static class YetAnotherClass
static class YetAnotherClass
{
public YetAnotherClass() {}
public ForwardReferenceClass frc;
Expand Down

0 comments on commit ef4115c

Please sign in to comment.