Skip to content

Commit b88ce67

Browse files
committed
Fix #1369
1 parent bc2271a commit b88ce67

File tree

7 files changed

+137
-77
lines changed

7 files changed

+137
-77
lines changed

release-notes/VERSION

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Project: jackson-databind
88

99
#1341: FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY
1010
(contributed by Connor K)
11+
#1369: Improve `@JsonCreator` detection via `AnnotationIntrospector`
12+
by passing `MappingConfig`
1113

1214
2.8.3 (not yet released)
1315

src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@
33
import java.lang.annotation.Annotation;
44
import java.util.*;
55

6-
import com.fasterxml.jackson.annotation.JsonCreator;
7-
import com.fasterxml.jackson.annotation.JsonFormat;
8-
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
9-
import com.fasterxml.jackson.annotation.JsonInclude;
10-
import com.fasterxml.jackson.annotation.JsonProperty;
6+
import com.fasterxml.jackson.annotation.*;
117
import com.fasterxml.jackson.core.Version;
128
import com.fasterxml.jackson.core.Versioned;
139
import com.fasterxml.jackson.databind.JsonDeserializer;
@@ -365,7 +361,7 @@ public VisibilityChecker<?> findAutoDetectVisibility(AnnotatedClass ac, Visibili
365361
* instantiating resolver builder, but also configuring it based on
366362
* relevant annotations (not including ones checked with a call to
367363
* {@link #findSubtypes}
368-
*
364+
*
369365
* @param config Configuration settings in effect (for serialization or deserialization)
370366
* @param ac Annotated class to check for annotations
371367
* @param baseType Base java type of value for which resolver is to be found
@@ -1393,7 +1389,10 @@ public boolean hasAnyGetterAnnotation(AnnotatedMethod am) {
13931389
*
13941390
* @return True if such annotation is found (and is not disabled),
13951391
* false otherwise
1392+
*
1393+
* @deprecated Since 2.9 use {@link #findCreatorAnnotation} instead.
13961394
*/
1395+
@Deprecated
13971396
public boolean hasCreatorAnnotation(Annotated a) {
13981397
return false;
13991398
}
@@ -1405,11 +1404,40 @@ public boolean hasCreatorAnnotation(Annotated a) {
14051404
* creator with implicit but no explicit name for the argument).
14061405
*
14071406
* @since 2.5
1407+
* @deprecated Since 2.9 use {@link #findCreatorAnnotation} instead.
14081408
*/
1409+
@Deprecated
14091410
public JsonCreator.Mode findCreatorBinding(Annotated a) {
14101411
return null;
14111412
}
1412-
1413+
1414+
/**
1415+
* Method called to check whether potential Creator (constructor or static factory
1416+
* method) has explicit annotation to indicate it as actual Creator; and if so,
1417+
* which {@link com.fasterxml.jackson.annotation.JsonCreator.Mode} to use.
1418+
*<p>
1419+
* NOTE: caller needs to consider possibility of both `null` (no annotation found)
1420+
* and {@link com.fasterxml.jackson.annotation.JsonCreator.Mode#DISABLED} (annotation found,
1421+
* but disabled); latter is necessary as marker in case multiple introspectors are chained,
1422+
* as well as possibly as when using mix-in annotations.
1423+
*
1424+
* @param config Configuration settings in effect (for serialization or deserialization)
1425+
* @param a Annotated accessor (usually constructor or static method) to check
1426+
*
1427+
* @since 2.9
1428+
*/
1429+
public JsonCreator.Mode findCreatorAnnotation(MapperConfig<?> config, Annotated a) {
1430+
// 13-Sep-2016, tatu: for backwards compatibility, implement using delegation
1431+
if (hasCreatorAnnotation(a)) {
1432+
JsonCreator.Mode mode = findCreatorBinding(a);
1433+
if (mode == null) {
1434+
mode = JsonCreator.Mode.DEFAULT;
1435+
}
1436+
return mode;
1437+
}
1438+
return null;
1439+
}
1440+
14131441
/*
14141442
/**********************************************************
14151443
/* Overridable methods: may be used as low-level extension

src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java

Lines changed: 45 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -403,22 +403,23 @@ public ValueInstantiator _valueInstantiatorInstance(DeserializationConfig config
403403
// in list of constructors, so needs to be handled separately.
404404
AnnotatedConstructor defaultCtor = beanDesc.findDefaultConstructor();
405405
if (defaultCtor != null) {
406-
if (!creators.hasDefaultCreator() || intr.hasCreatorAnnotation(defaultCtor)) {
406+
if (!creators.hasDefaultCreator() || _hasCreatorAnnotation(ctxt, defaultCtor)) {
407407
creators.setDefaultCreator(defaultCtor);
408408
}
409409
}
410410

411411
// may need to keep track for [#725]
412412
List<AnnotatedConstructor> implicitCtors = null;
413413
for (AnnotatedConstructor ctor : beanDesc.getConstructors()) {
414-
final boolean isCreator = intr.hasCreatorAnnotation(ctor);
414+
JsonCreator.Mode creatorMode = intr.findCreatorAnnotation(ctxt.getConfig(), ctor);
415+
final boolean isCreator = (creatorMode != null) && (creatorMode != JsonCreator.Mode.DISABLED);
415416
BeanPropertyDefinition[] propDefs = creatorParams.get(ctor);
416417
final int argCount = ctor.getParameterCount();
417418

418419
// some single-arg factory methods (String, number) are auto-detected
419420
if (argCount == 1) {
420421
BeanPropertyDefinition argDef = (propDefs == null) ? null : propDefs[0];
421-
boolean useProps = _checkIfCreatorPropertyBased(intr, ctor, argDef);
422+
boolean useProps = _checkIfCreatorPropertyBased(intr, ctor, argDef, creatorMode);
422423

423424
if (useProps) {
424425
SettableBeanProperty[] properties = new SettableBeanProperty[1];
@@ -583,36 +584,6 @@ protected void _checkImplicitlyNamedConstructors(DeserializationContext ctxt,
583584
}
584585
}
585586

586-
protected boolean _checkIfCreatorPropertyBased(AnnotationIntrospector intr,
587-
AnnotatedWithParams creator, BeanPropertyDefinition propDef)
588-
{
589-
JsonCreator.Mode mode = intr.findCreatorBinding(creator);
590-
591-
if (mode == JsonCreator.Mode.PROPERTIES) {
592-
return true;
593-
}
594-
if (mode == JsonCreator.Mode.DELEGATING) {
595-
return false;
596-
}
597-
// If explicit name, or inject id, property-based
598-
if (((propDef != null) && propDef.isExplicitlyNamed())
599-
|| (intr.findInjectableValueId(creator.getParameter(0)) != null)) {
600-
return true;
601-
}
602-
if (propDef != null) {
603-
// One more thing: if implicit name matches property with a getter
604-
// or field, we'll consider it property-based as well
605-
String implName = propDef.getName();
606-
if (implName != null && !implName.isEmpty()) {
607-
if (propDef.couldSerialize()) {
608-
return true;
609-
}
610-
}
611-
}
612-
// in absence of everything else, default to delegating
613-
return false;
614-
}
615-
616587
protected boolean _handleSingleArgumentConstructor(DeserializationContext ctxt,
617588
BeanDescription beanDesc, VisibilityChecker<?> vchecker,
618589
AnnotationIntrospector intr, CreatorCollector creators,
@@ -667,7 +638,8 @@ protected boolean _handleSingleArgumentConstructor(DeserializationContext ctxt,
667638
{
668639
final DeserializationConfig config = ctxt.getConfig();
669640
for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {
670-
final boolean isCreator = intr.hasCreatorAnnotation(factory);
641+
JsonCreator.Mode creatorMode = intr.findCreatorAnnotation(ctxt.getConfig(), factory);
642+
final boolean isCreator = (creatorMode != null) && (creatorMode != JsonCreator.Mode.DISABLED);
671643
final int argCount = factory.getParameterCount();
672644
// zero-arg methods must be annotated; if so, are "default creators" [JACKSON-850]
673645
if (argCount == 0) {
@@ -681,7 +653,7 @@ protected boolean _handleSingleArgumentConstructor(DeserializationContext ctxt,
681653
// some single-arg factory methods (String, number) are auto-detected
682654
if (argCount == 1) {
683655
BeanPropertyDefinition argDef = (propDefs == null) ? null : propDefs[0];
684-
boolean useProps = _checkIfCreatorPropertyBased(intr, factory, argDef);
656+
boolean useProps = _checkIfCreatorPropertyBased(intr, factory, argDef, creatorMode);
685657
if (!useProps) { // not property based but delegating
686658
/*boolean added=*/ _handleSingleArgumentFactory(config, beanDesc, vchecker, intr, creators,
687659
factory, isCreator);
@@ -891,22 +863,32 @@ protected PropertyName _findImplicitParamName(AnnotatedParameter param, Annotati
891863
return null;
892864
}
893865

894-
@Deprecated // in 2.6, remove from 2.7
895-
protected PropertyName _findExplicitParamName(AnnotatedParameter param, AnnotationIntrospector intr)
866+
protected boolean _checkIfCreatorPropertyBased(AnnotationIntrospector intr,
867+
AnnotatedWithParams creator, BeanPropertyDefinition propDef,
868+
JsonCreator.Mode creatorMode)
896869
{
897-
if (param != null && intr != null) {
898-
return intr.findNameForDeserialization(param);
870+
if (creatorMode == JsonCreator.Mode.PROPERTIES) {
871+
return true;
899872
}
900-
return null;
901-
}
902-
903-
@Deprecated // in 2.6, remove from 2.7
904-
protected boolean _hasExplicitParamName(AnnotatedParameter param, AnnotationIntrospector intr)
905-
{
906-
if (param != null && intr != null) {
907-
PropertyName n = intr.findNameForDeserialization(param);
908-
return (n != null) && n.hasSimpleName();
873+
if (creatorMode == JsonCreator.Mode.DELEGATING) {
874+
return false;
909875
}
876+
// If explicit name, or inject id, property-based
877+
if (((propDef != null) && propDef.isExplicitlyNamed())
878+
|| (intr.findInjectableValueId(creator.getParameter(0)) != null)) {
879+
return true;
880+
}
881+
if (propDef != null) {
882+
// One more thing: if implicit name matches property with a getter
883+
// or field, we'll consider it property-based as well
884+
String implName = propDef.getName();
885+
if (implName != null && !implName.isEmpty()) {
886+
if (propDef.couldSerialize()) {
887+
return true;
888+
}
889+
}
890+
}
891+
// in absence of everything else, default to delegating
910892
return false;
911893
}
912894

@@ -1240,7 +1222,7 @@ public JsonDeserializer<?> createEnumDeserializer(DeserializationContext ctxt,
12401222
: valueInstantiator.getFromObjectArguments(ctxt.getConfig());
12411223
// May have @JsonCreator for static factory method:
12421224
for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {
1243-
if (ctxt.getAnnotationIntrospector().hasCreatorAnnotation(factory)) {
1225+
if (_hasCreatorAnnotation(ctxt, factory)) {
12441226
int argCount = factory.getParameterCount();
12451227
if (argCount == 1) {
12461228
Class<?> returnType = factory.getRawReturnType();
@@ -1441,9 +1423,8 @@ private KeyDeserializer _createEnumKeyDeserializer(DeserializationContext ctxt,
14411423
}
14421424
EnumResolver enumRes = constructEnumResolver(enumClass, config, beanDesc.findJsonValueMethod());
14431425
// May have @JsonCreator for static factory method:
1444-
final AnnotationIntrospector ai = config.getAnnotationIntrospector();
14451426
for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {
1446-
if (ai.hasCreatorAnnotation(factory)) {
1427+
if (_hasCreatorAnnotation(ctxt, factory)) {
14471428
int argCount = factory.getParameterCount();
14481429
if (argCount == 1) {
14491430
Class<?> returnType = factory.getRawReturnType();
@@ -1874,6 +1855,19 @@ protected EnumResolver constructEnumResolver(Class<?> enumClass,
18741855
return EnumResolver.constructUnsafe(enumClass, config.getAnnotationIntrospector());
18751856
}
18761857

1858+
/**
1859+
* @since 2.9
1860+
*/
1861+
protected boolean _hasCreatorAnnotation(DeserializationContext ctxt,
1862+
Annotated ann) {
1863+
AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
1864+
if (intr != null) {
1865+
JsonCreator.Mode mode = intr.findCreatorAnnotation(ctxt.getConfig(), ann);
1866+
return (mode != null) && (mode != JsonCreator.Mode.DISABLED);
1867+
}
1868+
return false;
1869+
}
1870+
18771871
/*
18781872
/**********************************************************
18791873
/* Deprecated helper methods

src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -705,19 +705,27 @@ public boolean hasAnyGetterAnnotation(AnnotatedMethod am) {
705705
}
706706

707707
@Override
708+
@Deprecated // since 2.9
708709
public boolean hasCreatorAnnotation(Annotated a) {
709710
return _primary.hasCreatorAnnotation(a) || _secondary.hasCreatorAnnotation(a);
710711
}
711712

712713
@Override
714+
@Deprecated // since 2.9
713715
public JsonCreator.Mode findCreatorBinding(Annotated a) {
714716
JsonCreator.Mode mode = _primary.findCreatorBinding(a);
715717
if (mode != null) {
716718
return mode;
717719
}
718720
return _secondary.findCreatorBinding(a);
719721
}
720-
722+
723+
@Override
724+
public JsonCreator.Mode findCreatorAnnotation(MapperConfig<?> config, Annotated a) {
725+
JsonCreator.Mode mode = _primary.findCreatorAnnotation(config, a);
726+
return (mode == null) ? _secondary.findCreatorAnnotation(config, a) : mode;
727+
}
728+
721729
protected boolean _isExplicitClassOrOb(Object maybeCls, Class<?> implicit) {
722730
if (maybeCls == null) {
723731
return false;

src/main/java/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.lang.reflect.Method;
55
import java.util.*;
66

7+
import com.fasterxml.jackson.annotation.JsonCreator;
78
import com.fasterxml.jackson.annotation.JsonFormat;
89
import com.fasterxml.jackson.annotation.JsonInclude;
910

@@ -537,10 +538,8 @@ public Method findFactoryMethod(Class<?>... expArgTypes)
537538

538539
protected boolean isFactoryMethod(AnnotatedMethod am)
539540
{
540-
/* First: return type must be compatible with the introspected class
541-
* (i.e. allowed to be sub-class, although usually is the same
542-
* class)
543-
*/
541+
// First: return type must be compatible with the introspected class
542+
// (i.e. allowed to be sub-class, although usually is the same class)
544543
Class<?> rt = am.getRawReturnType();
545544
if (!getBeanClass().isAssignableFrom(rt)) {
546545
return false;
@@ -550,7 +549,8 @@ protected boolean isFactoryMethod(AnnotatedMethod am)
550549
* (a) marked with @JsonCreator annotation, or
551550
* (b) "valueOf" (at this point, need not be public)
552551
*/
553-
if (_annotationIntrospector.hasCreatorAnnotation(am)) {
552+
JsonCreator.Mode mode = _annotationIntrospector.findCreatorAnnotation(this._config, am);
553+
if ((mode != null) && (mode != JsonCreator.Mode.DISABLED)) {
554554
return true;
555555
}
556556
final String name = am.getName();

0 commit comments

Comments
 (0)