1 package com.fasterxml.jackson.databind.ser; 2 3 import java.io.IOException; 4 import java.lang.annotation.Annotation; 5 import java.lang.reflect.Field; 6 import java.lang.reflect.Method; 7 import java.lang.reflect.Type; 8 import java.util.HashMap; 9 10 import com.fasterxml.jackson.annotation.JsonInclude; 11 import com.fasterxml.jackson.core.JsonGenerator; 12 import com.fasterxml.jackson.core.SerializableString; 13 import com.fasterxml.jackson.core.io.SerializedString; 14 import com.fasterxml.jackson.databind.*; 15 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl; 16 import com.fasterxml.jackson.databind.introspect.*; 17 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; 18 import com.fasterxml.jackson.databind.jsonschema.SchemaAware; 19 import com.fasterxml.jackson.databind.jsontype.TypeSerializer; 20 import com.fasterxml.jackson.databind.node.ObjectNode; 21 import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap; 22 import com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanPropertyWriter; 23 import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase; 24 import com.fasterxml.jackson.databind.util.Annotations; 25 import com.fasterxml.jackson.databind.util.ClassUtil; 26 import com.fasterxml.jackson.databind.util.NameTransformer; 27 28 /** 29 * Base bean property handler class, which implements common parts of 30 * reflection-based functionality for accessing a property value and serializing 31 * it. 32 * <p> 33 * Note that current design tries to keep instances immutable (semi-functional 34 * style); mostly because these instances are exposed to application code and 35 * this is to reduce likelihood of data corruption and synchronization issues. 36 */ 37 @JacksonStdImpl 38 // since 2.6. NOTE: sub-classes typically are not 39 public class BeanPropertyWriter extends PropertyWriter // which extends 40 // `ConcreteBeanPropertyBase` 41 implements java.io.Serializable // since 2.6 42 { 43 // As of 2.7 44 private static final long serialVersionUID = 1L; 45 46 /** 47 * Marker object used to indicate "do not serialize if empty" 48 */ 49 public final static Object MARKER_FOR_EMPTY = JsonInclude.Include.NON_EMPTY; 50 51 /* 52 /*********************************************************** 53 /* Basic property metadata: name, type, other 54 /*********************************************************** 55 */ 56 57 /** 58 * Logical name of the property; will be used as the field name under which 59 * value for the property is written. 60 * <p> 61 * NOTE: do NOT change name of this field; it is accessed by Afterburner 62 * module (until 2.4; not directly from 2.5) ALSO NOTE: ... and while it 63 * really ought to be `SerializableString`, changing that is also 64 * binary-incompatible change. So nope. 65 */ 66 protected final SerializedString _name; 67 68 /** 69 * Wrapper name to use for this element, if any 70 * 71 * @since 2.2 72 */ 73 protected final PropertyName _wrapperName; 74 75 /** 76 * Type property is declared to have, either in class definition or 77 * associated annotations. 78 */ 79 protected final JavaType _declaredType; 80 81 /** 82 * Type to use for locating serializer; normally same as return type of the 83 * accessor method, but may be overridden by annotations. 84 */ 85 protected final JavaType _cfgSerializationType; 86 87 /** 88 * Base type of the property, if the declared type is "non-trivial"; meaning 89 * it is either a structured type (collection, map, array), or 90 * parameterized. Used to retain type information about contained type, 91 * which is mostly necessary if type meta-data is to be included. 92 */ 93 protected JavaType _nonTrivialBaseType; 94 95 /** 96 * Annotations from context (most often, class that declares property, or in 97 * case of sub-class serializer, from that sub-class) 98 * <p> 99 * NOTE: transient just to support JDK serializability; Annotations do not 100 * serialize. At all. 101 */ 102 protected final transient Annotations _contextAnnotations; 103 104 /* 105 /*********************************************************** 106 /* Settings for accessing property value to serialize 107 /*********************************************************** 108 */ 109 110 /** 111 * Member (field, method) that represents property and allows access to 112 * associated annotations. 113 */ 114 protected final AnnotatedMember _member; 115 116 /** 117 * Accessor method used to get property value, for method-accessible 118 * properties. Null if and only if {@link #_field} is null. 119 * <p> 120 * `transient` (and non-final) only to support JDK serializability. 121 */ 122 protected transient Method _accessorMethod; 123 124 /** 125 * Field that contains the property value for field-accessible properties. 126 * Null if and only if {@link #_accessorMethod} is null. 127 * <p> 128 * `transient` (and non-final) only to support JDK serializability. 129 */ 130 protected transient Field _field; 131 132 /* 133 /*********************************************************** 134 /* Serializers needed 135 /*********************************************************** 136 */ 137 138 /** 139 * Serializer to use for writing out the value: null if it cannot be known 140 * statically; non-null if it can. 141 */ 142 protected JsonSerializer<Object> _serializer; 143 144 /** 145 * Serializer used for writing out null values, if any: if null, null values 146 * are to be suppressed. 147 */ 148 protected JsonSerializer<Object> _nullSerializer; 149 150 /** 151 * If property being serialized needs type information to be included this 152 * is the type serializer to use. Declared type (possibly augmented with 153 * annotations) of property is used for determining exact mechanism to use 154 * (compared to actual runtime type used for serializing actual state). 155 */ 156 protected TypeSerializer _typeSerializer; 157 158 /** 159 * In case serializer is not known statically (i.e. <code>_serializer</code> 160 * is null), we will use a lookup structure for storing dynamically resolved 161 * mapping from type(s) to serializer(s). 162 */ 163 protected transient PropertySerializerMap _dynamicSerializers; 164 165 /* 166 /*********************************************************** 167 /* Filtering 168 /*********************************************************** 169 */ 170 171 /** 172 * Whether null values are to be suppressed (nothing written out if value is 173 * null) or not. Note that this is a configuration value during 174 * construction, and actual handling relies on setting (or not) of 175 * {@link #_nullSerializer}. 176 */ 177 protected final boolean _suppressNulls; 178 179 /** 180 * Value that is considered default value of the property; used for 181 * default-value-suppression if enabled. 182 */ 183 protected final Object _suppressableValue; 184 185 /** 186 * Alternate set of property writers used when view-based filtering is 187 * available for the Bean. 188 */ 189 protected final Class<?>[] _includeInViews; 190 191 /* 192 /********************************************************** 193 /* Opaqueinternal data that bean serializer factory and 194 /* bean serializers can add. 195 /********************************************************** 196 */ 197 198 protected transient HashMap<Object, Object> _internalSettings; 199 200 /* 201 /*********************************************************** 202 /* Construction, configuration 203 /*********************************************************** 204 */ 205 206 /** 207 * @since 2.9 (added `includeInViews` since 2.8) 208 */ 209 @SuppressWarnings("unchecked") BeanPropertyWriter(BeanPropertyDefinition propDef, AnnotatedMember member, Annotations contextAnnotations, JavaType declaredType, JsonSerializer<?> ser, TypeSerializer typeSer, JavaType serType, boolean suppressNulls, Object suppressableValue, Class<?>[] includeInViews)210 public BeanPropertyWriter(BeanPropertyDefinition propDef, 211 AnnotatedMember member, Annotations contextAnnotations, 212 JavaType declaredType, 213 JsonSerializer<?> ser, TypeSerializer typeSer, JavaType serType, 214 boolean suppressNulls, Object suppressableValue, 215 Class<?>[] includeInViews) 216 { 217 super(propDef); 218 _member = member; 219 _contextAnnotations = contextAnnotations; 220 221 _name = new SerializedString(propDef.getName()); 222 _wrapperName = propDef.getWrapperName(); 223 224 _declaredType = declaredType; 225 _serializer = (JsonSerializer<Object>) ser; 226 _dynamicSerializers = (ser == null) ? PropertySerializerMap 227 .emptyForProperties() : null; 228 _typeSerializer = typeSer; 229 _cfgSerializationType = serType; 230 231 if (member instanceof AnnotatedField) { 232 _accessorMethod = null; 233 _field = (Field) member.getMember(); 234 } else if (member instanceof AnnotatedMethod) { 235 _accessorMethod = (Method) member.getMember(); 236 _field = null; 237 } else { 238 // 01-Dec-2014, tatu: Used to be illegal, but now explicitly allowed 239 // for virtual props 240 _accessorMethod = null; 241 _field = null; 242 } 243 _suppressNulls = suppressNulls; 244 _suppressableValue = suppressableValue; 245 246 // this will be resolved later on, unless nulls are to be suppressed 247 _nullSerializer = null; 248 _includeInViews = includeInViews; 249 } 250 251 @Deprecated // Since 2.9 BeanPropertyWriter(BeanPropertyDefinition propDef, AnnotatedMember member, Annotations contextAnnotations, JavaType declaredType, JsonSerializer<?> ser, TypeSerializer typeSer, JavaType serType, boolean suppressNulls, Object suppressableValue)252 public BeanPropertyWriter(BeanPropertyDefinition propDef, 253 AnnotatedMember member, Annotations contextAnnotations, 254 JavaType declaredType, 255 JsonSerializer<?> ser, TypeSerializer typeSer, JavaType serType, 256 boolean suppressNulls, Object suppressableValue) 257 { 258 this(propDef, member, contextAnnotations, declaredType, 259 ser, typeSer, serType, suppressNulls, suppressableValue, 260 null); 261 } 262 263 /** 264 * Constructor that may be of use to virtual properties, when there is need 265 * for the zero-arg ("default") constructor, and actual initialization is 266 * done after constructor call. 267 * 268 * @since 2.5 269 */ BeanPropertyWriter()270 protected BeanPropertyWriter() { 271 super(PropertyMetadata.STD_REQUIRED_OR_OPTIONAL); 272 _member = null; 273 _contextAnnotations = null; 274 275 _name = null; 276 _wrapperName = null; 277 _includeInViews = null; 278 279 _declaredType = null; 280 _serializer = null; 281 _dynamicSerializers = null; 282 _typeSerializer = null; 283 _cfgSerializationType = null; 284 285 _accessorMethod = null; 286 _field = null; 287 _suppressNulls = false; 288 _suppressableValue = null; 289 290 _nullSerializer = null; 291 } 292 293 /** 294 * "Copy constructor" to be used by filtering sub-classes 295 */ BeanPropertyWriter(BeanPropertyWriter base)296 protected BeanPropertyWriter(BeanPropertyWriter base) { 297 this(base, base._name); 298 } 299 300 /** 301 * @since 2.5 302 */ BeanPropertyWriter(BeanPropertyWriter base, PropertyName name)303 protected BeanPropertyWriter(BeanPropertyWriter base, PropertyName name) { 304 super(base); 305 /* 306 * 02-Dec-2014, tatu: This is a big mess, alas, what with dependency to 307 * MapperConfig to encode, and Afterburner having heartburn for 308 * SerializableString (vs SerializedString). Hope it can be 309 * resolved/reworked in 2.6 timeframe, if not for 2.5 310 */ 311 _name = new SerializedString(name.getSimpleName()); 312 _wrapperName = base._wrapperName; 313 314 _contextAnnotations = base._contextAnnotations; 315 _declaredType = base._declaredType; 316 317 _member = base._member; 318 _accessorMethod = base._accessorMethod; 319 _field = base._field; 320 321 _serializer = base._serializer; 322 _nullSerializer = base._nullSerializer; 323 // one more thing: copy internal settings, if any 324 if (base._internalSettings != null) { 325 _internalSettings = new HashMap<Object, Object>( 326 base._internalSettings); 327 } 328 _cfgSerializationType = base._cfgSerializationType; 329 _dynamicSerializers = base._dynamicSerializers; 330 _suppressNulls = base._suppressNulls; 331 _suppressableValue = base._suppressableValue; 332 _includeInViews = base._includeInViews; 333 _typeSerializer = base._typeSerializer; 334 _nonTrivialBaseType = base._nonTrivialBaseType; 335 } 336 BeanPropertyWriter(BeanPropertyWriter base, SerializedString name)337 protected BeanPropertyWriter(BeanPropertyWriter base, SerializedString name) { 338 super(base); 339 _name = name; 340 _wrapperName = base._wrapperName; 341 342 _member = base._member; 343 _contextAnnotations = base._contextAnnotations; 344 _declaredType = base._declaredType; 345 _accessorMethod = base._accessorMethod; 346 _field = base._field; 347 _serializer = base._serializer; 348 _nullSerializer = base._nullSerializer; 349 if (base._internalSettings != null) { 350 _internalSettings = new HashMap<Object, Object>( 351 base._internalSettings); 352 } 353 _cfgSerializationType = base._cfgSerializationType; 354 _dynamicSerializers = base._dynamicSerializers; 355 _suppressNulls = base._suppressNulls; 356 _suppressableValue = base._suppressableValue; 357 _includeInViews = base._includeInViews; 358 _typeSerializer = base._typeSerializer; 359 _nonTrivialBaseType = base._nonTrivialBaseType; 360 } 361 rename(NameTransformer transformer)362 public BeanPropertyWriter rename(NameTransformer transformer) { 363 String newName = transformer.transform(_name.getValue()); 364 if (newName.equals(_name.toString())) { 365 return this; 366 } 367 return _new(PropertyName.construct(newName)); 368 } 369 370 /** 371 * Overridable factory method used by sub-classes 372 * 373 * @since 2.6 374 */ _new(PropertyName newName)375 protected BeanPropertyWriter _new(PropertyName newName) { 376 return new BeanPropertyWriter(this, newName); 377 } 378 379 /** 380 * Method called to set, reset or clear the configured type serializer for 381 * property. 382 * 383 * @since 2.6 384 */ assignTypeSerializer(TypeSerializer typeSer)385 public void assignTypeSerializer(TypeSerializer typeSer) { 386 _typeSerializer = typeSer; 387 } 388 389 /** 390 * Method called to assign value serializer for property 391 */ assignSerializer(JsonSerializer<Object> ser)392 public void assignSerializer(JsonSerializer<Object> ser) { 393 // may need to disable check in future? 394 if ((_serializer != null) && (_serializer != ser)) { 395 throw new IllegalStateException(String.format( 396 "Cannot override _serializer: had a %s, trying to set to %s", 397 ClassUtil.classNameOf(_serializer), ClassUtil.classNameOf(ser))); 398 } 399 _serializer = ser; 400 } 401 402 /** 403 * Method called to assign null value serializer for property 404 */ assignNullSerializer(JsonSerializer<Object> nullSer)405 public void assignNullSerializer(JsonSerializer<Object> nullSer) { 406 // may need to disable check in future? 407 if ((_nullSerializer != null) && (_nullSerializer != nullSer)) { 408 throw new IllegalStateException(String.format( 409 "Cannot override _nullSerializer: had a %s, trying to set to %s", 410 ClassUtil.classNameOf(_nullSerializer), ClassUtil.classNameOf(nullSer))); 411 } 412 _nullSerializer = nullSer; 413 } 414 415 /** 416 * Method called create an instance that handles details of unwrapping 417 * contained value. 418 */ unwrappingWriter(NameTransformer unwrapper)419 public BeanPropertyWriter unwrappingWriter(NameTransformer unwrapper) { 420 return new UnwrappingBeanPropertyWriter(this, unwrapper); 421 } 422 423 /** 424 * Method called to define type to consider as "non-trivial" basetype, 425 * needed for dynamic serialization resolution for complex (usually 426 * container) types 427 */ setNonTrivialBaseType(JavaType t)428 public void setNonTrivialBaseType(JavaType t) { 429 _nonTrivialBaseType = t; 430 } 431 432 /** 433 * Method called to ensure that the mutator has proper access rights to 434 * be called, as per configuration. Overridden by implementations that 435 * have mutators that require access, fields and setters. 436 * 437 * @since 2.8.3 438 */ fixAccess(SerializationConfig config)439 public void fixAccess(SerializationConfig config) { 440 _member.fixAccess(config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); 441 } 442 443 /* 444 /*********************************************************** 445 /* JDK Serializability 446 /*********************************************************** 447 */ 448 449 /* 450 * Ideally would not require mutable state, and instead would re-create with 451 * final settings. However, as things are, with sub-types and all, simplest 452 * to just change Field/Method value directly. 453 */ readResolve()454 Object readResolve() { 455 if (_member instanceof AnnotatedField) { 456 _accessorMethod = null; 457 _field = (Field) _member.getMember(); 458 } else if (_member instanceof AnnotatedMethod) { 459 _accessorMethod = (Method) _member.getMember(); 460 _field = null; 461 } 462 if (_serializer == null) { 463 _dynamicSerializers = PropertySerializerMap.emptyForProperties(); 464 } 465 return this; 466 } 467 468 /* 469 /************************************************************ 470 /* BeanProperty impl 471 /*********************************************************** 472 */ 473 474 // Note: also part of 'PropertyWriter' 475 @Override getName()476 public String getName() { 477 return _name.getValue(); 478 } 479 480 // Note: also part of 'PropertyWriter' 481 @Override getFullName()482 public PropertyName getFullName() { // !!! TODO: impl properly 483 return new PropertyName(_name.getValue()); 484 } 485 486 @Override getType()487 public JavaType getType() { 488 return _declaredType; 489 } 490 491 @Override getWrapperName()492 public PropertyName getWrapperName() { 493 return _wrapperName; 494 } 495 496 // Note: also part of 'PropertyWriter' 497 @Override getAnnotation(Class<A> acls)498 public <A extends Annotation> A getAnnotation(Class<A> acls) { 499 return (_member == null) ? null : _member.getAnnotation(acls); 500 } 501 502 // Note: also part of 'PropertyWriter' 503 @Override getContextAnnotation(Class<A> acls)504 public <A extends Annotation> A getContextAnnotation(Class<A> acls) { 505 return (_contextAnnotations == null) ? null : _contextAnnotations 506 .get(acls); 507 } 508 509 @Override getMember()510 public AnnotatedMember getMember() { 511 return _member; 512 } 513 514 // @since 2.3 -- needed so it can be overridden by unwrapping writer _depositSchemaProperty(ObjectNode propertiesNode, JsonNode schemaNode)515 protected void _depositSchemaProperty(ObjectNode propertiesNode, 516 JsonNode schemaNode) { 517 propertiesNode.set(getName(), schemaNode); 518 } 519 520 /* 521 /*********************************************************** 522 /* Managing and accessing of opaque internal settings 523 /* (used by extensions) 524 /*********************************************************** 525 */ 526 527 /** 528 * Method for accessing value of specified internal setting. 529 * 530 * @return Value of the setting, if any; null if none. 531 */ getInternalSetting(Object key)532 public Object getInternalSetting(Object key) { 533 return (_internalSettings == null) ? null : _internalSettings.get(key); 534 } 535 536 /** 537 * Method for setting specific internal setting to given value 538 * 539 * @return Old value of the setting, if any (null if none) 540 */ setInternalSetting(Object key, Object value)541 public Object setInternalSetting(Object key, Object value) { 542 if (_internalSettings == null) { 543 _internalSettings = new HashMap<Object, Object>(); 544 } 545 return _internalSettings.put(key, value); 546 } 547 548 /** 549 * Method for removing entry for specified internal setting. 550 * 551 * @return Existing value of the setting, if any (null if none) 552 */ removeInternalSetting(Object key)553 public Object removeInternalSetting(Object key) { 554 Object removed = null; 555 if (_internalSettings != null) { 556 removed = _internalSettings.remove(key); 557 // to reduce memory usage, let's also drop the Map itself, if empty 558 if (_internalSettings.size() == 0) { 559 _internalSettings = null; 560 } 561 } 562 return removed; 563 } 564 565 /* 566 /*********************************************************** 567 /* Accessors 568 /*********************************************************** 569 */ 570 getSerializedName()571 public SerializableString getSerializedName() { 572 return _name; 573 } 574 hasSerializer()575 public boolean hasSerializer() { 576 return _serializer != null; 577 } 578 hasNullSerializer()579 public boolean hasNullSerializer() { 580 return _nullSerializer != null; 581 } 582 583 /** 584 * @since 2.6 585 */ getTypeSerializer()586 public TypeSerializer getTypeSerializer() { 587 return _typeSerializer; 588 } 589 590 /** 591 * Accessor that will return true if this bean property has to support 592 * "unwrapping"; ability to replace POJO structural wrapping with optional 593 * name prefix and/or suffix (or in some cases, just removal of wrapper 594 * name). 595 * <p> 596 * Default implementation simply returns false. 597 * 598 * @since 2.3 599 */ isUnwrapping()600 public boolean isUnwrapping() { 601 return false; 602 } 603 willSuppressNulls()604 public boolean willSuppressNulls() { 605 return _suppressNulls; 606 } 607 608 /** 609 * Method called to check to see if this property has a name that would 610 * conflict with a given name. 611 * 612 * @since 2.6 613 */ wouldConflictWithName(PropertyName name)614 public boolean wouldConflictWithName(PropertyName name) { 615 if (_wrapperName != null) { 616 return _wrapperName.equals(name); 617 } 618 // Bit convoluted since our support for namespaces is spotty but: 619 return name.hasSimpleName(_name.getValue()) && !name.hasNamespace(); 620 } 621 622 // Needed by BeanSerializer#getSchema getSerializer()623 public JsonSerializer<Object> getSerializer() { 624 return _serializer; 625 } 626 getSerializationType()627 public JavaType getSerializationType() { 628 return _cfgSerializationType; 629 } 630 631 @Deprecated // since 2.9 getRawSerializationType()632 public Class<?> getRawSerializationType() { 633 return (_cfgSerializationType == null) ? null : _cfgSerializationType 634 .getRawClass(); 635 } 636 637 /** 638 * @deprecated Since 2.7, to be removed from 2.9, use {@link #getType()} instead. 639 */ 640 @Deprecated getPropertyType()641 public Class<?> getPropertyType() { 642 if (_accessorMethod != null) { 643 return _accessorMethod.getReturnType(); 644 } 645 if (_field != null) { 646 return _field.getType(); 647 } 648 return null; 649 } 650 651 /** 652 * Get the generic property type of this property writer. 653 * 654 * @return The property type, or null if not found. 655 * 656 * @deprecated Since 2.7, to be removed from 2.9, use {@link #getType()} instead. 657 */ 658 @Deprecated getGenericPropertyType()659 public Type getGenericPropertyType() { 660 if (_accessorMethod != null) { 661 return _accessorMethod.getGenericReturnType(); 662 } 663 if (_field != null) { 664 return _field.getGenericType(); 665 } 666 return null; 667 } 668 getViews()669 public Class<?>[] getViews() { 670 return _includeInViews; 671 } 672 673 /* 674 /*********************************************************** 675 /* PropertyWriter methods (serialization) 676 /*********************************************************** 677 */ 678 679 /** 680 * Method called to access property that this bean stands for, from within 681 * given bean, and to serialize it as a JSON Object field using appropriate 682 * serializer. 683 */ 684 @Override serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov)685 public void serializeAsField(Object bean, JsonGenerator gen, 686 SerializerProvider prov) throws Exception { 687 // inlined 'get()' 688 final Object value = (_accessorMethod == null) ? _field.get(bean) 689 : _accessorMethod.invoke(bean, (Object[]) null); 690 691 // Null handling is bit different, check that first 692 if (value == null) { 693 if (_nullSerializer != null) { 694 gen.writeFieldName(_name); 695 _nullSerializer.serialize(null, gen, prov); 696 } 697 return; 698 } 699 // then find serializer to use 700 JsonSerializer<Object> ser = _serializer; 701 if (ser == null) { 702 Class<?> cls = value.getClass(); 703 PropertySerializerMap m = _dynamicSerializers; 704 ser = m.serializerFor(cls); 705 if (ser == null) { 706 ser = _findAndAddDynamic(m, cls, prov); 707 } 708 } 709 // and then see if we must suppress certain values (default, empty) 710 if (_suppressableValue != null) { 711 if (MARKER_FOR_EMPTY == _suppressableValue) { 712 if (ser.isEmpty(prov, value)) { 713 return; 714 } 715 } else if (_suppressableValue.equals(value)) { 716 return; 717 } 718 } 719 // For non-nulls: simple check for direct cycles 720 if (value == bean) { 721 // four choices: exception; handled by call; pass-through or write null 722 if (_handleSelfReference(bean, gen, prov, ser)) { 723 return; 724 } 725 } 726 gen.writeFieldName(_name); 727 if (_typeSerializer == null) { 728 ser.serialize(value, gen, prov); 729 } else { 730 ser.serializeWithType(value, gen, prov, _typeSerializer); 731 } 732 } 733 734 /** 735 * Method called to indicate that serialization of a field was omitted due 736 * to filtering, in cases where backend data format does not allow basic 737 * omission. 738 * 739 * @since 2.3 740 */ 741 @Override serializeAsOmittedField(Object bean, JsonGenerator gen, SerializerProvider prov)742 public void serializeAsOmittedField(Object bean, JsonGenerator gen, 743 SerializerProvider prov) throws Exception { 744 if (!gen.canOmitFields()) { 745 gen.writeOmittedField(_name.getValue()); 746 } 747 } 748 749 /** 750 * Alternative to {@link #serializeAsField} that is used when a POJO is 751 * serialized as JSON Array; the difference is that no field names are 752 * written. 753 * 754 * @since 2.3 755 */ 756 @Override serializeAsElement(Object bean, JsonGenerator gen, SerializerProvider prov)757 public void serializeAsElement(Object bean, JsonGenerator gen, 758 SerializerProvider prov) throws Exception { 759 // inlined 'get()' 760 final Object value = (_accessorMethod == null) ? _field.get(bean) 761 : _accessorMethod.invoke(bean, (Object[]) null); 762 if (value == null) { // nulls need specialized handling 763 if (_nullSerializer != null) { 764 _nullSerializer.serialize(null, gen, prov); 765 } else { // can NOT suppress entries in tabular output 766 gen.writeNull(); 767 } 768 return; 769 } 770 // otherwise find serializer to use 771 JsonSerializer<Object> ser = _serializer; 772 if (ser == null) { 773 Class<?> cls = value.getClass(); 774 PropertySerializerMap map = _dynamicSerializers; 775 ser = map.serializerFor(cls); 776 if (ser == null) { 777 ser = _findAndAddDynamic(map, cls, prov); 778 } 779 } 780 // and then see if we must suppress certain values (default, empty) 781 if (_suppressableValue != null) { 782 if (MARKER_FOR_EMPTY == _suppressableValue) { 783 if (ser.isEmpty(prov, value)) { // can NOT suppress entries in 784 // tabular output 785 serializeAsPlaceholder(bean, gen, prov); 786 return; 787 } 788 } else if (_suppressableValue.equals(value)) { // can NOT suppress 789 // entries in tabular 790 // output 791 serializeAsPlaceholder(bean, gen, prov); 792 return; 793 } 794 } 795 // For non-nulls: simple check for direct cycles 796 if (value == bean) { 797 if (_handleSelfReference(bean, gen, prov, ser)) { 798 return; 799 } 800 } 801 if (_typeSerializer == null) { 802 ser.serialize(value, gen, prov); 803 } else { 804 ser.serializeWithType(value, gen, prov, _typeSerializer); 805 } 806 } 807 808 /** 809 * Method called to serialize a placeholder used in tabular output when real 810 * value is not to be included (is filtered out), but when we need an entry 811 * so that field indexes will not be off. Typically this should output null 812 * or empty String, depending on datatype. 813 * 814 * @since 2.1 815 */ 816 @Override serializeAsPlaceholder(Object bean, JsonGenerator gen, SerializerProvider prov)817 public void serializeAsPlaceholder(Object bean, JsonGenerator gen, 818 SerializerProvider prov) throws Exception { 819 if (_nullSerializer != null) { 820 _nullSerializer.serialize(null, gen, prov); 821 } else { 822 gen.writeNull(); 823 } 824 } 825 826 /* 827 /*********************************************************** 828 /* PropertyWriter methods (schema generation) 829 /*********************************************************** 830 */ 831 832 // Also part of BeanProperty implementation 833 @Override depositSchemaProperty(JsonObjectFormatVisitor v, SerializerProvider provider)834 public void depositSchemaProperty(JsonObjectFormatVisitor v, 835 SerializerProvider provider) throws JsonMappingException { 836 if (v != null) { 837 if (isRequired()) { 838 v.property(this); 839 } else { 840 v.optionalProperty(this); 841 } 842 } 843 } 844 845 // // // Legacy support for JsonFormatVisitable 846 847 /** 848 * Attempt to add the output of the given {@link BeanPropertyWriter} in the 849 * given {@link ObjectNode}. Otherwise, add the default schema 850 * {@link JsonNode} in place of the writer's output 851 * 852 * @param propertiesNode 853 * Node which the given property would exist within 854 * @param provider 855 * Provider that can be used for accessing dynamic aspects of 856 * serialization processing 857 */ 858 @Override 859 @Deprecated depositSchemaProperty(ObjectNode propertiesNode, SerializerProvider provider)860 public void depositSchemaProperty(ObjectNode propertiesNode, 861 SerializerProvider provider) throws JsonMappingException { 862 JavaType propType = getSerializationType(); 863 // 03-Dec-2010, tatu: SchemaAware REALLY should use JavaType, but alas 864 // it doesn't... 865 Type hint = (propType == null) ? getType() : propType.getRawClass(); 866 JsonNode schemaNode; 867 // Maybe it already has annotated/statically configured serializer? 868 JsonSerializer<Object> ser = getSerializer(); 869 if (ser == null) { // nope 870 ser = provider.findValueSerializer(getType(), this); 871 } 872 boolean isOptional = !isRequired(); 873 if (ser instanceof SchemaAware) { 874 schemaNode = ((SchemaAware) ser).getSchema(provider, hint, 875 isOptional); 876 } else { 877 schemaNode = com.fasterxml.jackson.databind.jsonschema.JsonSchema 878 .getDefaultSchemaNode(); 879 } 880 _depositSchemaProperty(propertiesNode, schemaNode); 881 } 882 883 /* 884 /********************************************************** 885 /* Helper methods 886 /********************************************************** 887 */ 888 _findAndAddDynamic( PropertySerializerMap map, Class<?> type, SerializerProvider provider)889 protected JsonSerializer<Object> _findAndAddDynamic( 890 PropertySerializerMap map, Class<?> type, 891 SerializerProvider provider) throws JsonMappingException { 892 PropertySerializerMap.SerializerAndMapResult result; 893 if (_nonTrivialBaseType != null) { 894 JavaType t = provider.constructSpecializedType(_nonTrivialBaseType, 895 type); 896 result = map.findAndAddPrimarySerializer(t, provider, this); 897 } else { 898 result = map.findAndAddPrimarySerializer(type, provider, this); 899 } 900 // did we get a new map of serializers? If so, start using it 901 if (map != result.map) { 902 _dynamicSerializers = result.map; 903 } 904 return result.serializer; 905 } 906 907 /** 908 * Method that can be used to access value of the property this Object 909 * describes, from given bean instance. 910 * <p> 911 * Note: method is final as it should not need to be overridden -- rather, 912 * calling method(s) ({@link #serializeAsField}) should be overridden to 913 * change the behavior 914 */ get(Object bean)915 public final Object get(Object bean) throws Exception { 916 return (_accessorMethod == null) ? _field.get(bean) : _accessorMethod 917 .invoke(bean, (Object[]) null); 918 } 919 920 /** 921 * Method called to handle a direct self-reference through this property. 922 * Method can choose to indicate an error by throwing 923 * {@link JsonMappingException}; fully handle serialization (and return 924 * true); or indicate that it should be serialized normally (return false). 925 * <p> 926 * Default implementation will throw {@link JsonMappingException} if 927 * {@link SerializationFeature#FAIL_ON_SELF_REFERENCES} is enabled; or 928 * return <code>false</code> if it is disabled. 929 * 930 * @return True if method fully handled self-referential value; false if not 931 * (caller is to handle it) or {@link JsonMappingException} if there 932 * is no way handle it 933 */ _handleSelfReference(Object bean, JsonGenerator gen, SerializerProvider prov, JsonSerializer<?> ser)934 protected boolean _handleSelfReference(Object bean, JsonGenerator gen, 935 SerializerProvider prov, JsonSerializer<?> ser) 936 throws IOException 937 { 938 if (!ser.usesObjectId()) { 939 if (prov.isEnabled(SerializationFeature.FAIL_ON_SELF_REFERENCES)) { 940 // 05-Feb-2013, tatu: Usually a problem, but NOT if we are handling 941 // object id; this may be the case for BeanSerializers at least. 942 // 13-Feb-2014, tatu: another possible ok case: custom serializer 943 // (something OTHER than {@link BeanSerializerBase} 944 if (ser instanceof BeanSerializerBase) { 945 prov.reportBadDefinition(getType(), "Direct self-reference leading to cycle"); 946 } 947 } else if (prov.isEnabled(SerializationFeature.WRITE_SELF_REFERENCES_AS_NULL)) { 948 if (_nullSerializer != null) { 949 // 23-Oct-2019, tatu: Tricky part -- caller does not specify if it's 950 // "as property" (in JSON Object) or "as element" (JSON array, via 951 // 'POJO-as-array'). And since Afterburner calls method can not easily 952 // start passing info either. So check generator to see... 953 // (note: not considering ROOT context as possibility, does not seem legal) 954 if (!gen.getOutputContext().inArray()) { 955 gen.writeFieldName(_name); 956 } 957 _nullSerializer.serialize(null, gen, prov); 958 } 959 return true; 960 } 961 } 962 return false; 963 } 964 965 @Override toString()966 public String toString() { 967 StringBuilder sb = new StringBuilder(40); 968 sb.append("property '").append(getName()).append("' ("); 969 if (_accessorMethod != null) { 970 sb.append("via method ") 971 .append(_accessorMethod.getDeclaringClass().getName()) 972 .append("#").append(_accessorMethod.getName()); 973 } else if (_field != null) { 974 sb.append("field \"").append(_field.getDeclaringClass().getName()) 975 .append("#").append(_field.getName()); 976 } else { 977 sb.append("virtual"); 978 } 979 if (_serializer == null) { 980 sb.append(", no static serializer"); 981 } else { 982 sb.append(", static serializer of type " 983 + _serializer.getClass().getName()); 984 } 985 sb.append(')'); 986 return sb.toString(); 987 } 988 } 989