1 package com.fasterxml.jackson.databind.ser.std; 2 3 import java.io.IOException; 4 import java.lang.reflect.Type; 5 import java.util.*; 6 7 import com.fasterxml.jackson.annotation.*; 8 9 import com.fasterxml.jackson.core.*; 10 import com.fasterxml.jackson.core.type.WritableTypeId; 11 12 import com.fasterxml.jackson.databind.*; 13 import com.fasterxml.jackson.databind.introspect.AnnotatedMember; 14 import com.fasterxml.jackson.databind.introspect.ObjectIdInfo; 15 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable; 16 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; 17 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; 18 import com.fasterxml.jackson.databind.jsonschema.JsonSerializableSchema; 19 import com.fasterxml.jackson.databind.jsonschema.SchemaAware; 20 import com.fasterxml.jackson.databind.jsontype.TypeSerializer; 21 import com.fasterxml.jackson.databind.node.ObjectNode; 22 import com.fasterxml.jackson.databind.ser.*; 23 import com.fasterxml.jackson.databind.ser.impl.MapEntrySerializer; 24 import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter; 25 import com.fasterxml.jackson.databind.ser.impl.PropertyBasedObjectIdGenerator; 26 import com.fasterxml.jackson.databind.ser.impl.WritableObjectId; 27 import com.fasterxml.jackson.databind.util.ArrayBuilders; 28 import com.fasterxml.jackson.databind.util.Converter; 29 import com.fasterxml.jackson.databind.util.IgnorePropertiesUtil; 30 import com.fasterxml.jackson.databind.util.NameTransformer; 31 32 /** 33 * Base class both for the standard bean serializer, and couple 34 * of variants that only differ in small details. 35 * Can be used for custom bean serializers as well, although that 36 * is not the primary design goal. 37 */ 38 @SuppressWarnings("serial") 39 public abstract class BeanSerializerBase 40 extends StdSerializer<Object> 41 implements ContextualSerializer, ResolvableSerializer, 42 JsonFormatVisitable, SchemaAware 43 { 44 protected final static PropertyName NAME_FOR_OBJECT_REF = new PropertyName("#object-ref"); 45 46 final protected static BeanPropertyWriter[] NO_PROPS = new BeanPropertyWriter[0]; 47 48 /* 49 /********************************************************** 50 /* Configuration 51 /********************************************************** 52 */ 53 54 /** 55 * @since 2.9 56 */ 57 final protected JavaType _beanType; 58 59 /** 60 * Writers used for outputting actual property values 61 */ 62 final protected BeanPropertyWriter[] _props; 63 64 /** 65 * Optional filters used to suppress output of properties that 66 * are only to be included in certain views 67 */ 68 final protected BeanPropertyWriter[] _filteredProps; 69 70 /** 71 * Handler for {@link com.fasterxml.jackson.annotation.JsonAnyGetter} 72 * annotated properties 73 */ 74 final protected AnyGetterWriter _anyGetterWriter; 75 76 /** 77 * Id of the bean property filter to use, if any; null if none. 78 */ 79 final protected Object _propertyFilterId; 80 81 /** 82 * If using custom type ids (usually via getter, or field), this is the 83 * reference to that member. 84 */ 85 final protected AnnotatedMember _typeId; 86 87 /** 88 * If this POJO can be alternatively serialized using just an object id 89 * to denote a reference to previously serialized object, 90 * this Object will handle details. 91 */ 92 final protected ObjectIdWriter _objectIdWriter; 93 94 /** 95 * Requested shape from bean class annotations. 96 */ 97 final protected JsonFormat.Shape _serializationShape; 98 99 /* 100 /********************************************************** 101 /* Life-cycle: constructors 102 /********************************************************** 103 */ 104 105 /** 106 * Constructor used by {@link BeanSerializerBuilder} to create an 107 * instance 108 * 109 * @param type Nominal type of values handled by this serializer 110 * @param builder Builder for accessing other collected information 111 */ BeanSerializerBase(JavaType type, BeanSerializerBuilder builder, BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties)112 protected BeanSerializerBase(JavaType type, BeanSerializerBuilder builder, 113 BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties) 114 { 115 super(type); 116 _beanType = type; 117 _props = properties; 118 _filteredProps = filteredProperties; 119 if (builder == null) { // mostly for testing 120 // 20-Sep-2019, tatu: Actually not just that but also "dummy" serializer for 121 // case of no bean properties, too 122 _typeId = null; 123 _anyGetterWriter = null; 124 _propertyFilterId = null; 125 _objectIdWriter = null; 126 _serializationShape = null; 127 } else { 128 _typeId = builder.getTypeId(); 129 _anyGetterWriter = builder.getAnyGetter(); 130 _propertyFilterId = builder.getFilterId(); 131 _objectIdWriter = builder.getObjectIdWriter(); 132 final JsonFormat.Value format = builder.getBeanDescription().findExpectedFormat(null); 133 _serializationShape = format.getShape(); 134 } 135 } 136 BeanSerializerBase(BeanSerializerBase src, BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties)137 protected BeanSerializerBase(BeanSerializerBase src, 138 BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties) 139 { 140 super(src._handledType); 141 _beanType = src._beanType; 142 _props = properties; 143 _filteredProps = filteredProperties; 144 145 _typeId = src._typeId; 146 _anyGetterWriter = src._anyGetterWriter; 147 _objectIdWriter = src._objectIdWriter; 148 _propertyFilterId = src._propertyFilterId; 149 _serializationShape = src._serializationShape; 150 } 151 BeanSerializerBase(BeanSerializerBase src, ObjectIdWriter objectIdWriter)152 protected BeanSerializerBase(BeanSerializerBase src, 153 ObjectIdWriter objectIdWriter) 154 { 155 this(src, objectIdWriter, src._propertyFilterId); 156 } 157 158 /** 159 * @since 2.3 160 */ BeanSerializerBase(BeanSerializerBase src, ObjectIdWriter objectIdWriter, Object filterId)161 protected BeanSerializerBase(BeanSerializerBase src, 162 ObjectIdWriter objectIdWriter, Object filterId) 163 { 164 super(src._handledType); 165 _beanType = src._beanType; 166 _props = src._props; 167 _filteredProps = src._filteredProps; 168 169 _typeId = src._typeId; 170 _anyGetterWriter = src._anyGetterWriter; 171 _objectIdWriter = objectIdWriter; 172 _propertyFilterId = filterId; 173 _serializationShape = src._serializationShape; 174 } 175 176 @Deprecated // since 2.8, remove soon BeanSerializerBase(BeanSerializerBase src, String[] toIgnore)177 protected BeanSerializerBase(BeanSerializerBase src, String[] toIgnore) 178 { 179 this(src, ArrayBuilders.arrayToSet(toIgnore), null); 180 } 181 182 @Deprecated // since 2.12 BeanSerializerBase(BeanSerializerBase src, Set<String> toIgnore)183 protected BeanSerializerBase(BeanSerializerBase src, Set<String> toIgnore) { 184 this(src, toIgnore, null); 185 } 186 BeanSerializerBase(BeanSerializerBase src, Set<String> toIgnore, Set<String> toInclude)187 protected BeanSerializerBase(BeanSerializerBase src, Set<String> toIgnore, Set<String> toInclude) 188 { 189 super(src._handledType); 190 191 _beanType = src._beanType; 192 final BeanPropertyWriter[] propsIn = src._props; 193 final BeanPropertyWriter[] fpropsIn = src._filteredProps; 194 final int len = propsIn.length; 195 196 ArrayList<BeanPropertyWriter> propsOut = new ArrayList<BeanPropertyWriter>(len); 197 ArrayList<BeanPropertyWriter> fpropsOut = (fpropsIn == null) ? null : new ArrayList<BeanPropertyWriter>(len); 198 199 for (int i = 0; i < len; ++i) { 200 BeanPropertyWriter bpw = propsIn[i]; 201 // should be ignored? 202 if (IgnorePropertiesUtil.shouldIgnore(bpw.getName(), toIgnore, toInclude)) { 203 continue; 204 } 205 propsOut.add(bpw); 206 if (fpropsIn != null) { 207 fpropsOut.add(fpropsIn[i]); 208 } 209 } 210 _props = propsOut.toArray(new BeanPropertyWriter[propsOut.size()]); 211 _filteredProps = (fpropsOut == null) ? null : fpropsOut.toArray(new BeanPropertyWriter[fpropsOut.size()]); 212 213 _typeId = src._typeId; 214 _anyGetterWriter = src._anyGetterWriter; 215 _objectIdWriter = src._objectIdWriter; 216 _propertyFilterId = src._propertyFilterId; 217 _serializationShape = src._serializationShape; 218 } 219 220 /** 221 * Mutant factory used for creating a new instance with different 222 * {@link ObjectIdWriter}. 223 * 224 * @since 2.0 225 */ withObjectIdWriter(ObjectIdWriter objectIdWriter)226 public abstract BeanSerializerBase withObjectIdWriter(ObjectIdWriter objectIdWriter); 227 228 /** 229 * Mutant factory used for creating a new instance with additional 230 * set of properties to ignore (from properties this instance otherwise has) 231 * 232 * @since 2.8 233 * @deprecated Since 2.12 234 */ 235 @Deprecated // since 2.12 withIgnorals(Set<String> toIgnore)236 protected BeanSerializerBase withIgnorals(Set<String> toIgnore) { 237 return withByNameInclusion(toIgnore, null); 238 } 239 240 /** 241 * Mutant factory used for creating a new instance with additional 242 * set of properties to ignore or include (from properties this instance otherwise has) 243 * 244 * @since 2.12 245 */ withByNameInclusion(Set<String> toIgnore, Set<String> toInclude)246 protected abstract BeanSerializerBase withByNameInclusion(Set<String> toIgnore, 247 Set<String> toInclude); 248 249 /** 250 * Mutant factory used for creating a new instance with additional 251 * set of properties to ignore (from properties this instance otherwise has) 252 * 253 * @deprecated since 2.8 254 */ 255 @Deprecated withIgnorals(String[] toIgnore)256 protected BeanSerializerBase withIgnorals(String[] toIgnore) { 257 return withIgnorals(ArrayBuilders.arrayToSet(toIgnore)); 258 } 259 260 /** 261 * Mutant factory for creating a variant that output POJO as a 262 * JSON Array. Implementations may ignore this request if output 263 * as array is not possible (either at all, or reliably). 264 * 265 * @since 2.1 266 */ asArraySerializer()267 protected abstract BeanSerializerBase asArraySerializer(); 268 269 /** 270 * Mutant factory used for creating a new instance with different 271 * filter id (used with <code>JsonFilter</code> annotation) 272 * 273 * @since 2.3 274 */ 275 @Override withFilterId(Object filterId)276 public abstract BeanSerializerBase withFilterId(Object filterId); 277 278 /** 279 * Mutant factory used for creating a new instance with modified set 280 * of properties. 281 *<p> 282 * Note: in 2.11.x, need to keep non-abstract for slightly better compatibility 283 * (XML module extends) 284 * 285 * @since 2.11.1 286 */ withProperties(BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties)287 protected abstract BeanSerializerBase withProperties(BeanPropertyWriter[] properties, 288 BeanPropertyWriter[] filteredProperties); 289 290 /** 291 * Copy-constructor that is useful for sub-classes that just want to 292 * copy all super-class properties without modifications. 293 */ BeanSerializerBase(BeanSerializerBase src)294 protected BeanSerializerBase(BeanSerializerBase src) { 295 this(src, src._props, src._filteredProps); 296 } 297 298 /** 299 * Copy-constructor that will also rename properties with given prefix 300 * (if it's non-empty) 301 */ BeanSerializerBase(BeanSerializerBase src, NameTransformer unwrapper)302 protected BeanSerializerBase(BeanSerializerBase src, NameTransformer unwrapper) { 303 this(src, rename(src._props, unwrapper), rename(src._filteredProps, unwrapper)); 304 } 305 rename(BeanPropertyWriter[] props, NameTransformer transformer)306 private final static BeanPropertyWriter[] rename(BeanPropertyWriter[] props, 307 NameTransformer transformer) 308 { 309 if (props == null || props.length == 0 || transformer == null || transformer == NameTransformer.NOP) { 310 return props; 311 } 312 final int len = props.length; 313 BeanPropertyWriter[] result = new BeanPropertyWriter[len]; 314 for (int i = 0; i < len; ++i) { 315 BeanPropertyWriter bpw = props[i]; 316 if (bpw != null) { 317 result[i] = bpw.rename(transformer); 318 } 319 } 320 return result; 321 } 322 323 /* 324 /********************************************************** 325 /* Post-construction processing: resolvable, contextual 326 /********************************************************** 327 */ 328 329 /** 330 * We need to implement {@link ResolvableSerializer} to be able to 331 * properly handle cyclic type references. 332 */ 333 @Override resolve(SerializerProvider provider)334 public void resolve(SerializerProvider provider) 335 throws JsonMappingException 336 { 337 int filteredCount = (_filteredProps == null) ? 0 : _filteredProps.length; 338 for (int i = 0, len = _props.length; i < len; ++i) { 339 BeanPropertyWriter prop = _props[i]; 340 // let's start with null serializer resolution actually 341 if (!prop.willSuppressNulls() && !prop.hasNullSerializer()) { 342 JsonSerializer<Object> nullSer = provider.findNullValueSerializer(prop); 343 if (nullSer != null) { 344 prop.assignNullSerializer(nullSer); 345 // also: remember to replace filtered property too? (see [JACKSON-364]) 346 if (i < filteredCount) { 347 BeanPropertyWriter w2 = _filteredProps[i]; 348 if (w2 != null) { 349 w2.assignNullSerializer(nullSer); 350 } 351 } 352 } 353 } 354 355 if (prop.hasSerializer()) { 356 continue; 357 } 358 // [databind#124]: allow use of converters 359 JsonSerializer<Object> ser = findConvertingSerializer(provider, prop); 360 if (ser == null) { 361 // Was the serialization type hard-coded? If so, use it 362 JavaType type = prop.getSerializationType(); 363 364 // It not, we can use declared return type if and only if declared type is final: 365 // if not, we don't really know the actual type until we get the instance. 366 if (type == null) { 367 type = prop.getType(); 368 if (!type.isFinal()) { 369 if (type.isContainerType() || type.containedTypeCount() > 0) { 370 prop.setNonTrivialBaseType(type); 371 } 372 continue; 373 } 374 } 375 ser = provider.findValueSerializer(type, prop); 376 // 04-Feb-2010, tatu: We may have stashed type serializer for content types 377 // too, earlier; if so, it's time to connect the dots here: 378 if (type.isContainerType()) { 379 TypeSerializer typeSer = type.getContentType().getTypeHandler(); 380 if (typeSer != null) { 381 // for now, can do this only for standard containers... 382 if (ser instanceof ContainerSerializer<?>) { 383 // ugly casts... but necessary 384 @SuppressWarnings("unchecked") 385 JsonSerializer<Object> ser2 = (JsonSerializer<Object>)((ContainerSerializer<?>) ser).withValueTypeSerializer(typeSer); 386 ser = ser2; 387 } 388 } 389 } 390 } 391 // and maybe replace filtered property too? 392 if (i < filteredCount) { 393 BeanPropertyWriter w2 = _filteredProps[i]; 394 if (w2 != null) { 395 w2.assignSerializer(ser); 396 // 17-Mar-2017, tatu: Typically will lead to chained call to original property, 397 // which would lead to double set. Not a problem itself, except... unwrapping 398 // may require work to be done, which does lead to an actual issue. 399 continue; 400 } 401 } 402 prop.assignSerializer(ser); 403 } 404 405 // also, any-getter may need to be resolved 406 if (_anyGetterWriter != null) { 407 // 23-Feb-2015, tatu: Misleading, as this actually triggers call to contextualization... 408 _anyGetterWriter.resolve(provider); 409 } 410 } 411 412 /** 413 * Helper method that can be used to see if specified property is annotated 414 * to indicate use of a converter for property value (in case of container types, 415 * it is container type itself, not key or content type). 416 * 417 * @since 2.2 418 */ findConvertingSerializer(SerializerProvider provider, BeanPropertyWriter prop)419 protected JsonSerializer<Object> findConvertingSerializer(SerializerProvider provider, 420 BeanPropertyWriter prop) 421 throws JsonMappingException 422 { 423 final AnnotationIntrospector intr = provider.getAnnotationIntrospector(); 424 if (intr != null) { 425 AnnotatedMember m = prop.getMember(); 426 if (m != null) { 427 Object convDef = intr.findSerializationConverter(m); 428 if (convDef != null) { 429 Converter<Object,Object> conv = provider.converterInstance(prop.getMember(), convDef); 430 JavaType delegateType = conv.getOutputType(provider.getTypeFactory()); 431 // [databind#731]: Should skip if nominally java.lang.Object 432 JsonSerializer<?> ser = delegateType.isJavaLangObject() ? null 433 : provider.findValueSerializer(delegateType, prop); 434 return new StdDelegatingSerializer(conv, delegateType, ser); 435 } 436 } 437 } 438 return null; 439 } 440 441 @SuppressWarnings("incomplete-switch") 442 @Override createContextual(SerializerProvider provider, BeanProperty property)443 public JsonSerializer<?> createContextual(SerializerProvider provider, 444 BeanProperty property) 445 throws JsonMappingException 446 { 447 final AnnotationIntrospector intr = provider.getAnnotationIntrospector(); 448 final AnnotatedMember accessor = (property == null || intr == null) 449 ? null : property.getMember(); 450 final SerializationConfig config = provider.getConfig(); 451 452 // Let's start with one big transmutation: Enums that are annotated 453 // to serialize as Objects may want to revert 454 JsonFormat.Value format = findFormatOverrides(provider, property, _handledType); 455 JsonFormat.Shape shape = null; 456 if ((format != null) && format.hasShape()) { 457 shape = format.getShape(); 458 // or, alternatively, asked to revert "back to" other representations... 459 if ((shape != JsonFormat.Shape.ANY) && (shape != _serializationShape)) { 460 if (_beanType.isEnumType()) { 461 switch (shape) { 462 case STRING: 463 case NUMBER: 464 case NUMBER_INT: 465 // 12-Oct-2014, tatu: May need to introspect full annotations... but 466 // for now, just do class ones 467 BeanDescription desc = config.introspectClassAnnotations(_beanType); 468 JsonSerializer<?> ser = EnumSerializer.construct(_beanType.getRawClass(), 469 provider.getConfig(), desc, format); 470 return provider.handlePrimaryContextualization(ser, property); 471 } 472 // 16-Oct-2016, tatu: Ditto for `Map`, `Map.Entry` subtypes 473 } else if (shape == JsonFormat.Shape.NATURAL) { 474 if (_beanType.isMapLikeType() && Map.class.isAssignableFrom(_handledType)) { 475 ; 476 } else if (Map.Entry.class.isAssignableFrom(_handledType)) { 477 JavaType mapEntryType = _beanType.findSuperType(Map.Entry.class); 478 479 JavaType kt = mapEntryType.containedTypeOrUnknown(0); 480 JavaType vt = mapEntryType.containedTypeOrUnknown(1); 481 482 // 16-Oct-2016, tatu: could have problems with type handling, as we do not 483 // see if "static" typing is needed, nor look for `TypeSerializer` yet... 484 JsonSerializer<?> ser = new MapEntrySerializer(_beanType, kt, vt, 485 false, null, property); 486 return provider.handlePrimaryContextualization(ser, property); 487 } 488 } 489 } 490 } 491 492 ObjectIdWriter oiw = _objectIdWriter; 493 494 // 16-Jun-2020, tatu: [databind#2759] means we need to handle reordering 495 // at a later point 496 int idPropOrigIndex = 0; 497 Set<String> ignoredProps = null; 498 Set<String> includedProps = null; 499 Object newFilterId = null; 500 501 // Then we may have an override for Object Id 502 if (accessor != null) { 503 ignoredProps = intr.findPropertyIgnoralByName(config, accessor).findIgnoredForSerialization(); 504 includedProps = intr.findPropertyInclusionByName(config, accessor).getIncluded(); 505 ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor); 506 if (objectIdInfo == null) { 507 // no ObjectId override, but maybe ObjectIdRef? 508 if (oiw != null) { 509 objectIdInfo = intr.findObjectReferenceInfo(accessor, null); 510 if (objectIdInfo != null) { 511 oiw = _objectIdWriter.withAlwaysAsId(objectIdInfo.getAlwaysAsId()); 512 } 513 } 514 } else { 515 // Ugh: mostly copied from BeanDeserializerBase: but can't easily change it 516 // to be able to move to SerializerProvider (where it really belongs) 517 518 // 2.1: allow modifications by "id ref" annotations as well: 519 objectIdInfo = intr.findObjectReferenceInfo(accessor, objectIdInfo); 520 Class<?> implClass = objectIdInfo.getGeneratorType(); 521 JavaType type = provider.constructType(implClass); 522 JavaType idType = provider.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0]; 523 // Property-based generator is trickier 524 if (implClass == ObjectIdGenerators.PropertyGenerator.class) { // most special one, needs extra work 525 String propName = objectIdInfo.getPropertyName().getSimpleName(); 526 BeanPropertyWriter idProp = null; 527 528 for (int i = 0, len = _props.length; ; ++i) { 529 if (i == len) { 530 provider.reportBadDefinition(_beanType, String.format( 531 "Invalid Object Id definition for %s: cannot find property with name '%s'", 532 handledType().getName(), propName)); 533 } 534 BeanPropertyWriter prop = _props[i]; 535 if (propName.equals(prop.getName())) { 536 idProp = prop; 537 // Let's mark id prop to be moved as the first (may still get rearranged) 538 // (although it may still get rearranged etc) 539 idPropOrigIndex = i; 540 break; 541 } 542 } 543 idType = idProp.getType(); 544 ObjectIdGenerator<?> gen = new PropertyBasedObjectIdGenerator(objectIdInfo, idProp); 545 oiw = ObjectIdWriter.construct(idType, (PropertyName) null, gen, objectIdInfo.getAlwaysAsId()); 546 } else { // other types need to be simpler 547 ObjectIdGenerator<?> gen = provider.objectIdGeneratorInstance(accessor, objectIdInfo); 548 oiw = ObjectIdWriter.construct(idType, objectIdInfo.getPropertyName(), gen, 549 objectIdInfo.getAlwaysAsId()); 550 } 551 } 552 // Or change Filter Id in use? 553 Object filterId = intr.findFilterId(accessor); 554 if (filterId != null) { 555 // but only consider case of adding a new filter id (no removal via annotation) 556 if (_propertyFilterId == null || !filterId.equals(_propertyFilterId)) { 557 newFilterId = filterId; 558 } 559 } 560 } 561 // either way, need to resolve serializer: 562 BeanSerializerBase contextual = this; 563 564 // 16-Jun-2020, tatu: [databind#2759] must make copies, then reorder 565 if (idPropOrigIndex > 0) { // note: must shuffle both regular properties and filtered 566 final BeanPropertyWriter[] newProps = Arrays.copyOf(_props, _props.length); 567 BeanPropertyWriter bpw = newProps[idPropOrigIndex]; 568 System.arraycopy(newProps, 0, newProps, 1, idPropOrigIndex); 569 newProps[0] = bpw; 570 final BeanPropertyWriter[] newFiltered; 571 if (_filteredProps == null) { 572 newFiltered = null; 573 } else { 574 newFiltered = Arrays.copyOf(_filteredProps, _filteredProps.length); 575 bpw = newFiltered[idPropOrigIndex]; 576 System.arraycopy(newFiltered, 0, newFiltered, 1, idPropOrigIndex); 577 newFiltered[0] = bpw; 578 } 579 contextual = contextual.withProperties(newProps, newFiltered); 580 } 581 582 if (oiw != null) { 583 JsonSerializer<?> ser = provider.findValueSerializer(oiw.idType, property); 584 oiw = oiw.withSerializer(ser); 585 if (oiw != _objectIdWriter) { 586 contextual = contextual.withObjectIdWriter(oiw); 587 } 588 } 589 // Possibly change inclusions: for ignored, only non-empty set matters; 590 // for inclusion `null` means "not defined" but empty "include nothing": 591 if (((ignoredProps != null) && !ignoredProps.isEmpty()) 592 || (includedProps != null)) { 593 contextual = contextual.withByNameInclusion(ignoredProps, includedProps); 594 } 595 if (newFilterId != null) { 596 contextual = contextual.withFilterId(newFilterId); 597 } 598 599 if (shape == null) { 600 shape = _serializationShape; 601 } 602 // last but not least; may need to transmute into as-array serialization 603 if (shape == JsonFormat.Shape.ARRAY) { 604 return contextual.asArraySerializer(); 605 } 606 return contextual; 607 } 608 609 /* 610 /********************************************************** 611 /* Accessors 612 /********************************************************** 613 */ 614 615 @Override properties()616 public Iterator<PropertyWriter> properties() { 617 return Arrays.<PropertyWriter>asList(_props).iterator(); 618 } 619 620 /* 621 /********************************************************** 622 /* Partial JsonSerializer implementation 623 /********************************************************** 624 */ 625 626 @Override usesObjectId()627 public boolean usesObjectId() { 628 return (_objectIdWriter != null); 629 } 630 631 // Main serialization method left unimplemented 632 @Override serialize(Object bean, JsonGenerator gen, SerializerProvider provider)633 public abstract void serialize(Object bean, JsonGenerator gen, SerializerProvider provider) 634 throws IOException; 635 636 // Type-info-augmented case implemented as it does not usually differ between impls 637 @Override serializeWithType(Object bean, JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer)638 public void serializeWithType(Object bean, JsonGenerator gen, 639 SerializerProvider provider, TypeSerializer typeSer) 640 throws IOException 641 { 642 if (_objectIdWriter != null) { 643 gen.setCurrentValue(bean); // [databind#631] 644 _serializeWithObjectId(bean, gen, provider, typeSer); 645 return; 646 } 647 648 gen.setCurrentValue(bean); // [databind#631] 649 WritableTypeId typeIdDef = _typeIdDef(typeSer, bean, JsonToken.START_OBJECT); 650 typeSer.writeTypePrefix(gen, typeIdDef); 651 if (_propertyFilterId != null) { 652 serializeFieldsFiltered(bean, gen, provider); 653 } else { 654 serializeFields(bean, gen, provider); 655 } 656 typeSer.writeTypeSuffix(gen, typeIdDef); 657 } 658 _serializeWithObjectId(Object bean, JsonGenerator gen, SerializerProvider provider, boolean startEndObject)659 protected final void _serializeWithObjectId(Object bean, JsonGenerator gen, SerializerProvider provider, 660 boolean startEndObject) throws IOException 661 { 662 final ObjectIdWriter w = _objectIdWriter; 663 WritableObjectId objectId = provider.findObjectId(bean, w.generator); 664 // If possible, write as id already 665 if (objectId.writeAsId(gen, provider, w)) { 666 return; 667 } 668 // If not, need to inject the id: 669 Object id = objectId.generateId(bean); 670 if (w.alwaysAsId) { 671 w.serializer.serialize(id, gen, provider); 672 return; 673 } 674 if (startEndObject) { 675 gen.writeStartObject(bean); 676 } 677 objectId.writeAsField(gen, provider, w); 678 if (_propertyFilterId != null) { 679 serializeFieldsFiltered(bean, gen, provider); 680 } else { 681 serializeFields(bean, gen, provider); 682 } 683 if (startEndObject) { 684 gen.writeEndObject(); 685 } 686 } 687 _serializeWithObjectId(Object bean, JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer)688 protected final void _serializeWithObjectId(Object bean, JsonGenerator gen, SerializerProvider provider, 689 TypeSerializer typeSer) throws IOException 690 { 691 final ObjectIdWriter w = _objectIdWriter; 692 WritableObjectId objectId = provider.findObjectId(bean, w.generator); 693 // If possible, write as id already 694 if (objectId.writeAsId(gen, provider, w)) { 695 return; 696 } 697 // If not, need to inject the id: 698 Object id = objectId.generateId(bean); 699 if (w.alwaysAsId) { 700 w.serializer.serialize(id, gen, provider); 701 return; 702 } 703 _serializeObjectId(bean, gen, provider, typeSer, objectId); 704 } 705 _serializeObjectId(Object bean, JsonGenerator g, SerializerProvider provider, TypeSerializer typeSer, WritableObjectId objectId)706 protected void _serializeObjectId(Object bean, JsonGenerator g, 707 SerializerProvider provider, 708 TypeSerializer typeSer, WritableObjectId objectId) throws IOException 709 { 710 final ObjectIdWriter w = _objectIdWriter; 711 WritableTypeId typeIdDef = _typeIdDef(typeSer, bean, JsonToken.START_OBJECT); 712 713 typeSer.writeTypePrefix(g, typeIdDef); 714 objectId.writeAsField(g, provider, w); 715 if (_propertyFilterId != null) { 716 serializeFieldsFiltered(bean, g, provider); 717 } else { 718 serializeFields(bean, g, provider); 719 } 720 typeSer.writeTypeSuffix(g, typeIdDef); 721 } 722 723 /** 724 * @since 2.9 725 */ _typeIdDef(TypeSerializer typeSer, Object bean, JsonToken valueShape)726 protected final WritableTypeId _typeIdDef(TypeSerializer typeSer, 727 Object bean, JsonToken valueShape) { 728 if (_typeId == null) { 729 return typeSer.typeId(bean, valueShape); 730 } 731 Object typeId = _typeId.getValue(bean); 732 if (typeId == null) { 733 // 28-Jun-2017, tatu: Is this really needed? Unchanged from 2.8 but... 734 typeId = ""; 735 } 736 return typeSer.typeId(bean, valueShape, typeId); 737 } 738 739 @Deprecated // since 2.9 _customTypeId(Object bean)740 protected final String _customTypeId(Object bean) 741 { 742 final Object typeId = _typeId.getValue(bean); 743 if (typeId == null) { 744 return ""; 745 } 746 return (typeId instanceof String) ? (String) typeId : typeId.toString(); 747 } 748 749 /* 750 /********************************************************** 751 /* Field serialization methods 752 /********************************************************** 753 */ 754 serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider)755 protected void serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider) 756 throws IOException 757 { 758 final BeanPropertyWriter[] props; 759 if (_filteredProps != null && provider.getActiveView() != null) { 760 props = _filteredProps; 761 } else { 762 props = _props; 763 } 764 int i = 0; 765 try { 766 for (final int len = props.length; i < len; ++i) { 767 BeanPropertyWriter prop = props[i]; 768 if (prop != null) { // can have nulls in filtered list 769 prop.serializeAsField(bean, gen, provider); 770 } 771 } 772 if (_anyGetterWriter != null) { 773 _anyGetterWriter.getAndSerialize(bean, gen, provider); 774 } 775 } catch (Exception e) { 776 String name = (i == props.length) ? "[anySetter]" : props[i].getName(); 777 wrapAndThrow(provider, e, bean, name); 778 } catch (StackOverflowError e) { 779 // 04-Sep-2009, tatu: Dealing with this is tricky, since we don't have many 780 // stack frames to spare... just one or two; can't make many calls. 781 782 // 10-Dec-2015, tatu: and due to above, avoid "from" method, call ctor directly: 783 //JsonMappingException mapE = JsonMappingException.from(gen, "Infinite recursion (StackOverflowError)", e); 784 JsonMappingException mapE = new JsonMappingException(gen, "Infinite recursion (StackOverflowError)", e); 785 786 String name = (i == props.length) ? "[anySetter]" : props[i].getName(); 787 mapE.prependPath(new JsonMappingException.Reference(bean, name)); 788 throw mapE; 789 } 790 } 791 792 /** 793 * Alternative serialization method that gets called when there is a 794 * {@link PropertyFilter} that needs to be called to determine 795 * which properties are to be serialized (and possibly how) 796 */ serializeFieldsFiltered(Object bean, JsonGenerator gen, SerializerProvider provider)797 protected void serializeFieldsFiltered(Object bean, JsonGenerator gen, 798 SerializerProvider provider) 799 throws IOException, JsonGenerationException 800 { 801 /* note: almost verbatim copy of "serializeFields"; copied (instead of merged) 802 * so that old method need not add check for existence of filter. 803 */ 804 final BeanPropertyWriter[] props; 805 if (_filteredProps != null && provider.getActiveView() != null) { 806 props = _filteredProps; 807 } else { 808 props = _props; 809 } 810 final PropertyFilter filter = findPropertyFilter(provider, _propertyFilterId, bean); 811 // better also allow missing filter actually.. 812 if (filter == null) { 813 serializeFields(bean, gen, provider); 814 return; 815 } 816 int i = 0; 817 try { 818 for (final int len = props.length; i < len; ++i) { 819 BeanPropertyWriter prop = props[i]; 820 if (prop != null) { // can have nulls in filtered list 821 filter.serializeAsField(bean, gen, provider, prop); 822 } 823 } 824 if (_anyGetterWriter != null) { 825 _anyGetterWriter.getAndFilter(bean, gen, provider, filter); 826 } 827 } catch (Exception e) { 828 String name = (i == props.length) ? "[anySetter]" : props[i].getName(); 829 wrapAndThrow(provider, e, bean, name); 830 } catch (StackOverflowError e) { 831 // Minimize call depth since we are close to fail: 832 //JsonMappingException mapE = JsonMappingException.from(gen, "Infinite recursion (StackOverflowError)", e); 833 JsonMappingException mapE = new JsonMappingException(gen, "Infinite recursion (StackOverflowError)", e); 834 String name = (i == props.length) ? "[anySetter]" : props[i].getName(); 835 mapE.prependPath(new JsonMappingException.Reference(bean, name)); 836 throw mapE; 837 } 838 } 839 840 @Deprecated 841 @Override getSchema(SerializerProvider provider, Type typeHint)842 public JsonNode getSchema(SerializerProvider provider, Type typeHint) 843 throws JsonMappingException 844 { 845 ObjectNode o = createSchemaNode("object", true); 846 // [JACKSON-813]: Add optional JSON Schema id attribute, if found 847 // NOTE: not optimal, does NOT go through AnnotationIntrospector etc: 848 JsonSerializableSchema ann = _handledType.getAnnotation(JsonSerializableSchema.class); 849 if (ann != null) { 850 String id = ann.id(); 851 if (id != null && id.length() > 0) { 852 o.put("id", id); 853 } 854 } 855 856 //todo: should the classname go in the title? 857 //o.put("title", _className); 858 ObjectNode propertiesNode = o.objectNode(); 859 final PropertyFilter filter; 860 if (_propertyFilterId != null) { 861 filter = findPropertyFilter(provider, _propertyFilterId, null); 862 } else { 863 filter = null; 864 } 865 866 for (int i = 0; i < _props.length; i++) { 867 BeanPropertyWriter prop = _props[i]; 868 if (filter == null) { 869 prop.depositSchemaProperty(propertiesNode, provider); 870 } else { 871 filter.depositSchemaProperty(prop, propertiesNode, provider); 872 } 873 874 } 875 o.set("properties", propertiesNode); 876 return o; 877 } 878 879 @Override acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)880 public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) 881 throws JsonMappingException 882 { 883 //deposit your output format 884 if (visitor == null) { 885 return; 886 } 887 JsonObjectFormatVisitor objectVisitor = visitor.expectObjectFormat(typeHint); 888 if (objectVisitor == null) { 889 return; 890 } 891 final SerializerProvider provider = visitor.getProvider(); 892 if (_propertyFilterId != null) { 893 PropertyFilter filter = findPropertyFilter(visitor.getProvider(), 894 _propertyFilterId, null); 895 for (int i = 0, end = _props.length; i < end; ++i) { 896 filter.depositSchemaProperty(_props[i], objectVisitor, provider); 897 } 898 } else { 899 Class<?> view = ((_filteredProps == null) || (provider == null)) 900 ? null : provider.getActiveView(); 901 final BeanPropertyWriter[] props; 902 if (view != null) { 903 props = _filteredProps; 904 } else { 905 props = _props; 906 } 907 908 for (int i = 0, end = props.length; i < end; ++i) { 909 BeanPropertyWriter prop = props[i]; 910 if (prop != null) { // may be filtered out unconditionally 911 prop.depositSchemaProperty(objectVisitor, provider); 912 } 913 } 914 } 915 } 916 } 917