1 package com.fasterxml.jackson.databind.deser; 2 3 import java.util.*; 4 5 import com.fasterxml.jackson.annotation.JsonFormat; 6 import com.fasterxml.jackson.databind.*; 7 import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 8 import com.fasterxml.jackson.databind.deser.impl.BeanPropertyMap; 9 import com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty; 10 import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader; 11 import com.fasterxml.jackson.databind.deser.impl.ValueInjector; 12 import com.fasterxml.jackson.databind.introspect.*; 13 import com.fasterxml.jackson.databind.util.Annotations; 14 import com.fasterxml.jackson.databind.util.ClassUtil; 15 import com.fasterxml.jackson.databind.util.IgnorePropertiesUtil; 16 17 /** 18 * Builder class used for aggregating deserialization information about 19 * a POJO, in order to build a {@link JsonDeserializer} for deserializing 20 * instances. 21 */ 22 public class BeanDeserializerBuilder 23 { 24 /* 25 /********************************************************** 26 /* Configuration 27 /********************************************************** 28 */ 29 30 final protected DeserializationConfig _config; 31 32 /** 33 * @since 2.9 34 */ 35 final protected DeserializationContext _context; 36 37 /* 38 /********************************************************** 39 /* General information about POJO 40 /********************************************************** 41 */ 42 43 /** 44 * Introspected information about POJO for deserializer to handle 45 */ 46 final protected BeanDescription _beanDesc; 47 48 /* 49 /********************************************************** 50 /* Accumulated information about properties 51 /********************************************************** 52 */ 53 54 /** 55 * Properties to deserialize collected so far. 56 */ 57 final protected Map<String, SettableBeanProperty> _properties 58 = new LinkedHashMap<String, SettableBeanProperty>(); 59 60 /** 61 * Value injectors for deserialization 62 */ 63 protected List<ValueInjector> _injectables; 64 65 /** 66 * Back-reference properties this bean contains (if any) 67 */ 68 protected HashMap<String, SettableBeanProperty> _backRefProperties; 69 70 /** 71 * Set of names of properties that are recognized but are to be ignored for deserialization 72 * purposes (meaning no exception is thrown, value is just skipped). 73 */ 74 protected HashSet<String> _ignorableProps; 75 76 /** 77 * Set of names of properties that are recognized and are set to be included for deserialization 78 * purposes (null deactivate this, empty includes nothing). 79 */ 80 protected HashSet<String> _includableProps; 81 82 /** 83 * Object that will handle value instantiation for the bean type. 84 */ 85 protected ValueInstantiator _valueInstantiator; 86 87 /** 88 * Handler for Object Id values, if Object Ids are enabled for the 89 * bean type. 90 */ 91 protected ObjectIdReader _objectIdReader; 92 93 /** 94 * Fallback setter used for handling any properties that are not 95 * mapped to regular setters. If setter is not null, it will be 96 * called once for each such property. 97 */ 98 protected SettableAnyProperty _anySetter; 99 100 /** 101 * Flag that can be set to ignore and skip unknown properties. 102 * If set, will not throw an exception for unknown properties. 103 */ 104 protected boolean _ignoreAllUnknown; 105 106 /** 107 * When creating Builder-based deserializers, this indicates 108 * method to call on builder to finalize value. 109 */ 110 protected AnnotatedMethod _buildMethod; 111 112 /** 113 * In addition, Builder may have additional configuration 114 */ 115 protected JsonPOJOBuilder.Value _builderConfig; 116 117 /* 118 /********************************************************** 119 /* Life-cycle: construction 120 /********************************************************** 121 */ 122 BeanDeserializerBuilder(BeanDescription beanDesc, DeserializationContext ctxt)123 public BeanDeserializerBuilder(BeanDescription beanDesc, 124 DeserializationContext ctxt) 125 { 126 _beanDesc = beanDesc; 127 _context = ctxt; 128 _config = ctxt.getConfig(); 129 } 130 131 /** 132 * Copy constructor for sub-classes to use, when constructing 133 * custom builder instances 134 */ BeanDeserializerBuilder(BeanDeserializerBuilder src)135 protected BeanDeserializerBuilder(BeanDeserializerBuilder src) 136 { 137 _beanDesc = src._beanDesc; 138 _context = src._context; 139 _config = src._config; 140 141 // let's make copy of properties 142 _properties.putAll(src._properties); 143 _injectables = _copy(src._injectables); 144 _backRefProperties = _copy(src._backRefProperties); 145 // Hmmh. Should we create defensive copies here? For now, not yet 146 _ignorableProps = src._ignorableProps; 147 _includableProps = src._includableProps; 148 _valueInstantiator = src._valueInstantiator; 149 _objectIdReader = src._objectIdReader; 150 151 _anySetter = src._anySetter; 152 _ignoreAllUnknown = src._ignoreAllUnknown; 153 154 _buildMethod = src._buildMethod; 155 _builderConfig = src._builderConfig; 156 } 157 _copy(HashMap<String, SettableBeanProperty> src)158 private static HashMap<String, SettableBeanProperty> _copy(HashMap<String, SettableBeanProperty> src) { 159 return (src == null) ? null 160 : new HashMap<String, SettableBeanProperty>(src); 161 } 162 _copy(List<T> src)163 private static <T> List<T> _copy(List<T> src) { 164 return (src == null) ? null : new ArrayList<T>(src); 165 } 166 167 /* 168 /********************************************************** 169 /* Life-cycle: state modification (adders, setters) 170 /********************************************************** 171 */ 172 173 /** 174 * Method for adding a new property or replacing a property. 175 */ addOrReplaceProperty(SettableBeanProperty prop, boolean allowOverride)176 public void addOrReplaceProperty(SettableBeanProperty prop, boolean allowOverride) { 177 _properties.put(prop.getName(), prop); 178 } 179 180 /** 181 * Method to add a property setter. Will ensure that there is no 182 * unexpected override; if one is found will throw a 183 * {@link IllegalArgumentException}. 184 */ addProperty(SettableBeanProperty prop)185 public void addProperty(SettableBeanProperty prop) 186 { 187 SettableBeanProperty old = _properties.put(prop.getName(), prop); 188 if (old != null && old != prop) { // should never occur... 189 throw new IllegalArgumentException("Duplicate property '"+prop.getName()+"' for "+_beanDesc.getType()); 190 } 191 } 192 193 /** 194 * Method called to add a property that represents so-called back reference; 195 * reference that "points back" to object that has forward reference to 196 * currently built bean. 197 */ addBackReferenceProperty(String referenceName, SettableBeanProperty prop)198 public void addBackReferenceProperty(String referenceName, SettableBeanProperty prop) 199 { 200 if (_backRefProperties == null) { 201 _backRefProperties = new HashMap<String, SettableBeanProperty>(4); 202 } 203 // 15-Sep-2016, tatu: For some reason fixing access at point of `build()` does 204 // NOT work (2 failing unit tests). Not 100% clear why, but for now force 205 // access set early; unfortunate, but since it works.... 206 if (_config.canOverrideAccessModifiers()) { 207 prop.fixAccess(_config); 208 } 209 _backRefProperties.put(referenceName, prop); 210 // 16-Jan-2018, tatu: As per [databind#1878] we may want to leave it as is, to allow 211 // population for cases of "wrong direction", traversing parent first 212 // If this causes problems should probably instead include in "ignored properties" list 213 // Alternatively could also extend annotation to allow/disallow explicit value from input 214 /* 215 if (_properties != null) { 216 _properties.remove(prop.getName()); 217 } 218 */ 219 } 220 addInjectable(PropertyName propName, JavaType propType, Annotations contextAnnotations, AnnotatedMember member, Object valueId)221 public void addInjectable(PropertyName propName, JavaType propType, 222 Annotations contextAnnotations, AnnotatedMember member, 223 Object valueId) 224 { 225 if (_injectables == null) { 226 _injectables = new ArrayList<ValueInjector>(); 227 } 228 if ( _config.canOverrideAccessModifiers()) { 229 member.fixAccess(_config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); 230 } 231 _injectables.add(new ValueInjector(propName, propType, member, valueId)); 232 } 233 234 /** 235 * Method that will add property name as one of properties that can 236 * be ignored if not recognized. 237 */ addIgnorable(String propName)238 public void addIgnorable(String propName) 239 { 240 if (_ignorableProps == null) { 241 _ignorableProps = new HashSet<String>(); 242 } 243 _ignorableProps.add(propName); 244 } 245 246 /** 247 * Method that will add property name as one of the properties that will be included. 248 * 249 * @since 2.12 250 */ addIncludable(String propName)251 public void addIncludable(String propName) 252 { 253 if (_includableProps == null) { 254 _includableProps = new HashSet<>(); 255 } 256 _includableProps.add(propName); 257 } 258 259 /** 260 * Method called by deserializer factory, when a "creator property" 261 * (something that is passed via constructor- or factory method argument; 262 * instead of setter or field). 263 *<p> 264 * Default implementation does not do anything; we may need to revisit this 265 * decision if these properties need to be available through accessors. 266 * For now, however, we just have to ensure that we don't try to resolve 267 * types that masked setter/field has (see [JACKSON-700] for details). 268 */ addCreatorProperty(SettableBeanProperty prop)269 public void addCreatorProperty(SettableBeanProperty prop) 270 { 271 addProperty(prop); 272 } 273 setAnySetter(SettableAnyProperty s)274 public void setAnySetter(SettableAnyProperty s) 275 { 276 if (_anySetter != null && s != null) { 277 throw new IllegalStateException("_anySetter already set to non-null"); 278 } 279 _anySetter = s; 280 } 281 setIgnoreUnknownProperties(boolean ignore)282 public void setIgnoreUnknownProperties(boolean ignore) { 283 _ignoreAllUnknown = ignore; 284 } 285 setValueInstantiator(ValueInstantiator inst)286 public void setValueInstantiator(ValueInstantiator inst) { 287 _valueInstantiator = inst; 288 } 289 setObjectIdReader(ObjectIdReader r)290 public void setObjectIdReader(ObjectIdReader r) { 291 _objectIdReader = r; 292 } 293 setPOJOBuilder(AnnotatedMethod buildMethod, JsonPOJOBuilder.Value config)294 public void setPOJOBuilder(AnnotatedMethod buildMethod, JsonPOJOBuilder.Value config) { 295 _buildMethod = buildMethod; 296 _builderConfig = config; 297 } 298 299 /* 300 /********************************************************** 301 /* Public accessors 302 /********************************************************** 303 */ 304 305 /** 306 * Method that allows accessing all properties that this 307 * builder currently contains. 308 *<p> 309 * Note that properties are returned in order that properties 310 * are ordered (explictly, or by rule), which is the serialization 311 * order. 312 */ getProperties()313 public Iterator<SettableBeanProperty> getProperties() { 314 return _properties.values().iterator(); 315 } 316 findProperty(PropertyName propertyName)317 public SettableBeanProperty findProperty(PropertyName propertyName) { 318 return _properties.get(propertyName.getSimpleName()); 319 } 320 hasProperty(PropertyName propertyName)321 public boolean hasProperty(PropertyName propertyName) { 322 return findProperty(propertyName) != null; 323 } 324 removeProperty(PropertyName name)325 public SettableBeanProperty removeProperty(PropertyName name) { 326 return _properties.remove(name.getSimpleName()); 327 } 328 getAnySetter()329 public SettableAnyProperty getAnySetter() { 330 return _anySetter; 331 } 332 getValueInstantiator()333 public ValueInstantiator getValueInstantiator() { 334 return _valueInstantiator; 335 } 336 getInjectables()337 public List<ValueInjector> getInjectables() { 338 return _injectables; 339 } 340 getObjectIdReader()341 public ObjectIdReader getObjectIdReader() { 342 return _objectIdReader; 343 } 344 getBuildMethod()345 public AnnotatedMethod getBuildMethod() { 346 return _buildMethod; 347 } 348 getBuilderConfig()349 public JsonPOJOBuilder.Value getBuilderConfig() { 350 return _builderConfig; 351 } 352 353 /** 354 * @since 2.9.4 355 */ hasIgnorable(String name)356 public boolean hasIgnorable(String name) { 357 return IgnorePropertiesUtil.shouldIgnore(name, _ignorableProps, _includableProps); 358 } 359 360 /* 361 /********************************************************** 362 /* Build method(s) 363 /********************************************************** 364 */ 365 366 /** 367 * Method for constructing a {@link BeanDeserializer}, given all 368 * information collected. 369 */ build()370 public JsonDeserializer<?> build() 371 { 372 Collection<SettableBeanProperty> props = _properties.values(); 373 _fixAccess(props); 374 BeanPropertyMap propertyMap = BeanPropertyMap.construct(_config, props, 375 _collectAliases(props), 376 _findCaseInsensitivity()); 377 propertyMap.assignIndexes(); 378 379 // view processing must be enabled if: 380 // (a) fields are not included by default (when deserializing with view), OR 381 // (b) one of properties has view(s) to included in defined 382 boolean anyViews = !_config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION); 383 if (!anyViews) { 384 for (SettableBeanProperty prop : props) { 385 if (prop.hasViews()) { 386 anyViews = true; 387 break; 388 } 389 } 390 } 391 392 // one more thing: may need to create virtual ObjectId property: 393 if (_objectIdReader != null) { 394 /* 18-Nov-2012, tatu: May or may not have annotations for id property; 395 * but no easy access. But hard to see id property being optional, 396 * so let's consider required at this point. 397 */ 398 ObjectIdValueProperty prop = new ObjectIdValueProperty(_objectIdReader, PropertyMetadata.STD_REQUIRED); 399 propertyMap = propertyMap.withProperty(prop); 400 } 401 402 return new BeanDeserializer(this, 403 _beanDesc, propertyMap, _backRefProperties, _ignorableProps, _ignoreAllUnknown, _includableProps, 404 anyViews); 405 } 406 407 /** 408 * Alternate build method used when we must be using some form of 409 * abstract resolution, usually by using addition Type Id 410 * ("polymorphic deserialization") 411 * 412 * @since 2.0 413 */ buildAbstract()414 public AbstractDeserializer buildAbstract() { 415 return new AbstractDeserializer(this, _beanDesc, _backRefProperties, _properties); 416 } 417 418 /** 419 * Method for constructing a specialized deserializer that uses 420 * additional external Builder object during data binding. 421 */ buildBuilderBased(JavaType valueType, String expBuildMethodName)422 public JsonDeserializer<?> buildBuilderBased(JavaType valueType, String expBuildMethodName) 423 throws JsonMappingException 424 { 425 // First: validation; must have build method that returns compatible type 426 if (_buildMethod == null) { 427 // as per [databind#777], allow empty name 428 if (!expBuildMethodName.isEmpty()) { 429 _context.reportBadDefinition(_beanDesc.getType(), 430 String.format("Builder class %s does not have build method (name: '%s')", 431 ClassUtil.getTypeDescription(_beanDesc.getType()), 432 expBuildMethodName)); 433 } 434 } else { 435 // also: type of the method must be compatible 436 Class<?> rawBuildType = _buildMethod.getRawReturnType(); 437 Class<?> rawValueType = valueType.getRawClass(); 438 if ((rawBuildType != rawValueType) 439 && !rawBuildType.isAssignableFrom(rawValueType) 440 && !rawValueType.isAssignableFrom(rawBuildType)) { 441 _context.reportBadDefinition(_beanDesc.getType(), 442 String.format("Build method `%s` has wrong return type (%s), not compatible with POJO type (%s)", 443 _buildMethod.getFullName(), 444 ClassUtil.getClassDescription(rawBuildType), 445 ClassUtil.getTypeDescription(valueType))); 446 } 447 } 448 // And if so, we can try building the deserializer 449 Collection<SettableBeanProperty> props = _properties.values(); 450 _fixAccess(props); 451 BeanPropertyMap propertyMap = BeanPropertyMap.construct(_config, props, 452 _collectAliases(props), 453 _findCaseInsensitivity()); 454 propertyMap.assignIndexes(); 455 456 boolean anyViews = !_config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION); 457 458 if (!anyViews) { 459 for (SettableBeanProperty prop : props) { 460 if (prop.hasViews()) { 461 anyViews = true; 462 break; 463 } 464 } 465 } 466 467 if (_objectIdReader != null) { 468 // May or may not have annotations for id property; but no easy access. 469 // But hard to see id property being optional, so let's consider required at this point. 470 ObjectIdValueProperty prop = new ObjectIdValueProperty(_objectIdReader, 471 PropertyMetadata.STD_REQUIRED); 472 propertyMap = propertyMap.withProperty(prop); 473 } 474 475 return createBuilderBasedDeserializer(valueType, propertyMap, anyViews); 476 } 477 478 /** 479 * Extension point for overriding the actual creation of the builder deserializer. 480 * 481 * @since 2.11 482 */ createBuilderBasedDeserializer(JavaType valueType, BeanPropertyMap propertyMap, boolean anyViews)483 protected JsonDeserializer<?> createBuilderBasedDeserializer(JavaType valueType, 484 BeanPropertyMap propertyMap, boolean anyViews) { 485 return new BuilderBasedDeserializer(this, 486 _beanDesc, valueType, propertyMap, _backRefProperties, _ignorableProps, _ignoreAllUnknown, 487 _includableProps, anyViews); 488 } 489 490 /* 491 /********************************************************** 492 /* Internal helper method(s) 493 /********************************************************** 494 */ 495 _fixAccess(Collection<SettableBeanProperty> mainProps)496 protected void _fixAccess(Collection<SettableBeanProperty> mainProps) 497 { 498 /* 07-Sep-2016, tatu: Ideally we should be able to avoid forcing 499 * access to properties that are likely ignored, but due to 500 * renaming it seems this is not a safe thing to do (there was 501 * at least one failing test). May need to dig deeper in future; 502 * for now let's just play it safe. 503 */ 504 /* 505 Set<String> ignorable = _ignorableProps; 506 if (ignorable == null) { 507 ignorable = Collections.emptySet(); 508 } 509 */ 510 511 // 17-Jun-2020, tatu: [databind#2760] means we should not force access 512 // if we are not configured to... at least not "regular" properties 513 514 if (_config.canOverrideAccessModifiers()) { 515 for (SettableBeanProperty prop : mainProps) { 516 /* 517 // first: no point forcing access on to-be-ignored properties 518 if (ignorable.contains(prop.getName())) { 519 continue; 520 } 521 */ 522 prop.fixAccess(_config); 523 } 524 } 525 526 // 15-Sep-2016, tatu: Access via back-ref properties has been done earlier 527 // as it has to, for some reason, so not repeated here. 528 /* 529 if (_backRefProperties != null) { 530 for (SettableBeanProperty prop : _backRefProperties.values()) { 531 prop.fixAccess(_config); 532 } 533 } 534 */ 535 536 // 17-Jun-2020, tatu: Despite [databind#2760], it seems that methods that 537 // are explicitly defined (any setter via annotation, builder too) can not 538 // be left as-is? May reconsider based on feedback 539 540 if (_anySetter != null) { 541 _anySetter.fixAccess(_config); 542 } 543 if (_buildMethod != null) { 544 _buildMethod.fixAccess(_config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); 545 } 546 } 547 _collectAliases(Collection<SettableBeanProperty> props)548 protected Map<String,List<PropertyName>> _collectAliases(Collection<SettableBeanProperty> props) 549 { 550 Map<String,List<PropertyName>> mapping = null; 551 AnnotationIntrospector intr = _config.getAnnotationIntrospector(); 552 if (intr != null) { 553 for (SettableBeanProperty prop : props) { 554 List<PropertyName> aliases = intr.findPropertyAliases(prop.getMember()); 555 if ((aliases == null) || aliases.isEmpty()) { 556 continue; 557 } 558 if (mapping == null) { 559 mapping = new HashMap<>(); 560 } 561 mapping.put(prop.getName(), aliases); 562 } 563 } 564 if (mapping == null) { 565 return Collections.emptyMap(); 566 } 567 return mapping; 568 } 569 570 // @since 2.12 _findCaseInsensitivity()571 protected boolean _findCaseInsensitivity() { 572 // 07-May-2020, tatu: First find combination of per-type config overrides (higher 573 // precedence) and per-type annotations (lower): 574 JsonFormat.Value format = _beanDesc.findExpectedFormat(null); 575 // and see if any of those has explicit definition; if not, use global baseline default 576 Boolean B = format.getFeature(JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES); 577 return (B == null) ? _config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES) 578 : B.booleanValue(); 579 } 580 } 581