1 package com.fasterxml.jackson.databind.ser; 2 3 import java.io.IOException; 4 import java.util.*; 5 import java.util.concurrent.atomic.AtomicReference; 6 7 import com.fasterxml.jackson.annotation.ObjectIdGenerator; 8 import com.fasterxml.jackson.core.JsonGenerator; 9 import com.fasterxml.jackson.databind.*; 10 import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; 11 import com.fasterxml.jackson.databind.introspect.Annotated; 12 import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; 13 import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper; 14 import com.fasterxml.jackson.databind.jsonschema.SchemaAware; 15 import com.fasterxml.jackson.databind.jsontype.TypeSerializer; 16 import com.fasterxml.jackson.databind.node.ObjectNode; 17 import com.fasterxml.jackson.databind.ser.impl.WritableObjectId; 18 import com.fasterxml.jackson.databind.util.ClassUtil; 19 20 /** 21 * Standard implementation used by {@link ObjectMapper}: 22 * adds methods only exposed to {@link ObjectMapper}, 23 * as well as constructors. 24 *<p> 25 * Note that class is abstract just because it does not 26 * define {@link #createInstance} method. 27 *<p> 28 * Also note that all custom {@link SerializerProvider} 29 * implementations must sub-class this class: {@link ObjectMapper} 30 * requires this type, not basic provider type. 31 */ 32 public abstract class DefaultSerializerProvider 33 extends SerializerProvider 34 implements java.io.Serializable // since 2.1; only because ObjectWriter needs it 35 { 36 private static final long serialVersionUID = 1L; 37 38 /* 39 /********************************************************** 40 /* State, for non-blueprint instances 41 /********************************************************** 42 */ 43 44 /** 45 * Per-serialization map Object Ids that have seen so far, iff 46 * Object Id handling is enabled. 47 */ 48 protected transient Map<Object, WritableObjectId> _seenObjectIds; 49 50 protected transient ArrayList<ObjectIdGenerator<?>> _objectIdGenerators; 51 52 /** 53 * Generator used for serialization. Needed mostly for error reporting 54 * purposes. 55 * 56 * @since 2.8 57 */ 58 protected transient JsonGenerator _generator; 59 60 /* 61 /********************************************************** 62 /* Life-cycle 63 /********************************************************** 64 */ 65 DefaultSerializerProvider()66 protected DefaultSerializerProvider() { super(); } 67 DefaultSerializerProvider(SerializerProvider src, SerializationConfig config,SerializerFactory f)68 protected DefaultSerializerProvider(SerializerProvider src, 69 SerializationConfig config,SerializerFactory f) { 70 super(src, config, f); 71 } 72 DefaultSerializerProvider(DefaultSerializerProvider src)73 protected DefaultSerializerProvider(DefaultSerializerProvider src) { 74 super(src); 75 } 76 77 /** 78 * Method that sub-classes need to implement: used to create a non-blueprint instances 79 * from the blueprint. 80 * This is needed to retain state during serialization. 81 */ createInstance(SerializationConfig config, SerializerFactory jsf)82 public abstract DefaultSerializerProvider createInstance(SerializationConfig config, 83 SerializerFactory jsf); 84 85 /** 86 * Method needed to ensure that {@link ObjectMapper#copy} will work 87 * properly; specifically, that caches are cleared, but settings 88 * will otherwise remain identical; and that no sharing of state 89 * occurs. 90 * 91 * @since 2.5 92 */ copy()93 public DefaultSerializerProvider copy() { 94 throw new IllegalStateException("DefaultSerializerProvider sub-class not overriding copy()"); 95 } 96 97 /* 98 /********************************************************** 99 /* Abstract method impls, factory methods 100 /********************************************************** 101 */ 102 103 @Override serializerInstance(Annotated annotated, Object serDef)104 public JsonSerializer<Object> serializerInstance(Annotated annotated, Object serDef) 105 throws JsonMappingException 106 { 107 if (serDef == null) { 108 return null; 109 } 110 JsonSerializer<?> ser; 111 112 if (serDef instanceof JsonSerializer) { 113 ser = (JsonSerializer<?>) serDef; 114 } else { 115 // Alas, there's no way to force return type of "either class 116 // X or Y" -- need to throw an exception after the fact 117 if (!(serDef instanceof Class)) { 118 reportBadDefinition(annotated.getType(), 119 "AnnotationIntrospector returned serializer definition of type " 120 +serDef.getClass().getName()+"; expected type JsonSerializer or Class<JsonSerializer> instead"); 121 } 122 Class<?> serClass = (Class<?>)serDef; 123 // there are some known "no class" markers to consider too: 124 if (serClass == JsonSerializer.None.class || ClassUtil.isBogusClass(serClass)) { 125 return null; 126 } 127 if (!JsonSerializer.class.isAssignableFrom(serClass)) { 128 reportBadDefinition(annotated.getType(), 129 "AnnotationIntrospector returned Class " 130 +serClass.getName()+"; expected Class<JsonSerializer>"); 131 } 132 HandlerInstantiator hi = _config.getHandlerInstantiator(); 133 ser = (hi == null) ? null : hi.serializerInstance(_config, annotated, serClass); 134 if (ser == null) { 135 ser = (JsonSerializer<?>) ClassUtil.createInstance(serClass, 136 _config.canOverrideAccessModifiers()); 137 } 138 } 139 return (JsonSerializer<Object>) _handleResolvable(ser); 140 } 141 142 @Override includeFilterInstance(BeanPropertyDefinition forProperty, Class<?> filterClass)143 public Object includeFilterInstance(BeanPropertyDefinition forProperty, 144 Class<?> filterClass) 145 { 146 if (filterClass == null) { 147 return null; 148 } 149 HandlerInstantiator hi = _config.getHandlerInstantiator(); 150 Object filter = (hi == null) ? null : hi.includeFilterInstance(_config, forProperty, filterClass); 151 if (filter == null) { 152 filter = ClassUtil.createInstance(filterClass, 153 _config.canOverrideAccessModifiers()); 154 } 155 return filter; 156 } 157 158 @Override includeFilterSuppressNulls(Object filter)159 public boolean includeFilterSuppressNulls(Object filter) throws JsonMappingException 160 { 161 if (filter == null) { 162 return true; 163 } 164 // should let filter decide what to do with nulls: 165 // But just case, let's handle unexpected (from our perspective) problems explicitly 166 try { 167 return filter.equals(null); 168 } catch (Throwable t) { 169 String msg = String.format( 170 "Problem determining whether filter of type '%s' should filter out `null` values: (%s) %s", 171 filter.getClass().getName(), t.getClass().getName(), ClassUtil.exceptionMessage(t)); 172 reportBadDefinition(filter.getClass(), msg, t); 173 return false; // never gets here 174 } 175 } 176 177 /* 178 /********************************************************** 179 /* Object Id handling 180 /********************************************************** 181 */ 182 183 @Override findObjectId(Object forPojo, ObjectIdGenerator<?> generatorType)184 public WritableObjectId findObjectId(Object forPojo, ObjectIdGenerator<?> generatorType) 185 { 186 if (_seenObjectIds == null) { 187 _seenObjectIds = _createObjectIdMap(); 188 } else { 189 WritableObjectId oid = _seenObjectIds.get(forPojo); 190 if (oid != null) { 191 return oid; 192 } 193 } 194 // Not seen yet; must add an entry, return it. For that, we need generator 195 ObjectIdGenerator<?> generator = null; 196 197 if (_objectIdGenerators == null) { 198 _objectIdGenerators = new ArrayList<ObjectIdGenerator<?>>(8); 199 } else { 200 for (int i = 0, len = _objectIdGenerators.size(); i < len; ++i) { 201 ObjectIdGenerator<?> gen = _objectIdGenerators.get(i); 202 if (gen.canUseFor(generatorType)) { 203 generator = gen; 204 break; 205 } 206 } 207 } 208 if (generator == null) { 209 generator = generatorType.newForSerialization(this); 210 _objectIdGenerators.add(generator); 211 } 212 WritableObjectId oid = new WritableObjectId(generator); 213 _seenObjectIds.put(forPojo, oid); 214 return oid; 215 } 216 217 /** 218 * Overridable helper method used for creating {@link java.util.Map} 219 * used for storing mappings from serializable objects to their 220 * Object Ids. 221 * 222 * @since 2.3 223 */ _createObjectIdMap()224 protected Map<Object,WritableObjectId> _createObjectIdMap() 225 { 226 /* 06-Aug-2013, tatu: We may actually want to use equality, 227 * instead of identity... so: 228 */ 229 if (isEnabled(SerializationFeature.USE_EQUALITY_FOR_OBJECT_ID)) { 230 return new HashMap<Object,WritableObjectId>(); 231 } 232 return new IdentityHashMap<Object,WritableObjectId>(); 233 } 234 235 /* 236 /********************************************************** 237 /* Extended API: simple accesors 238 /********************************************************** 239 */ 240 241 /** 242 * Method that can be called to see if this serializer provider 243 * can find a serializer for an instance of given class. 244 *<p> 245 * Note that no Exceptions are thrown, including unchecked ones: 246 * implementations are to swallow exceptions if necessary. 247 */ hasSerializerFor(Class<?> cls, AtomicReference<Throwable> cause)248 public boolean hasSerializerFor(Class<?> cls, AtomicReference<Throwable> cause) 249 { 250 // 07-Nov-2015, tatu: One special case, Object.class; will work only if 251 // empty beans are allowed or custom serializer registered. Easiest to 252 // check here. 253 if (cls == Object.class) { 254 if (!_config.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)) { 255 return true; 256 } 257 } 258 259 try { 260 JsonSerializer<?> ser = _findExplicitUntypedSerializer(cls); 261 return (ser != null); 262 } catch (JsonMappingException e) { 263 if (cause != null) { 264 cause.set(e); 265 } 266 } catch (RuntimeException e) { 267 if (cause == null) { // earlier behavior 268 throw e; 269 } 270 cause.set(e); 271 } 272 return false; 273 } 274 275 /** 276 * Accessor for the {@link JsonGenerator} currently in use for serializing 277 * content. Null for blueprint instances; non-null for actual active 278 * provider instances. 279 * 280 * @since 2.8 281 */ 282 @Override getGenerator()283 public JsonGenerator getGenerator() { 284 return _generator; 285 } 286 287 /* 288 /********************************************************** 289 /* Extended API called by ObjectMapper: value serialization 290 /********************************************************** 291 */ 292 293 /** 294 * The method to be called by {@link ObjectMapper} and {@link ObjectWriter} 295 * for serializing given value, using serializers that 296 * this provider has access to (via caching and/or creating new serializers 297 * as need be). 298 */ serializeValue(JsonGenerator gen, Object value)299 public void serializeValue(JsonGenerator gen, Object value) throws IOException 300 { 301 _generator = gen; 302 if (value == null) { 303 _serializeNull(gen); 304 return; 305 } 306 final Class<?> cls = value.getClass(); 307 // true, since we do want to cache root-level typed serializers (ditto for null property) 308 final JsonSerializer<Object> ser = findTypedValueSerializer(cls, true, null); 309 PropertyName rootName = _config.getFullRootName(); 310 if (rootName == null) { // not explicitly specified 311 if (_config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE)) { 312 _serialize(gen, value, ser, _config.findRootName(cls)); 313 return; 314 } 315 } else if (!rootName.isEmpty()) { 316 _serialize(gen, value, ser, rootName); 317 return; 318 } 319 _serialize(gen, value, ser); 320 } 321 322 /** 323 * The method to be called by {@link ObjectMapper} and {@link ObjectWriter} 324 * for serializing given value (assumed to be of specified root type, 325 * instead of runtime type of value), 326 * using serializers that 327 * this provider has access to (via caching and/or creating new serializers 328 * as need be), 329 * 330 * @param rootType Type to use for locating serializer to use, instead of actual 331 * runtime type. Must be actual type, or one of its super types 332 */ serializeValue(JsonGenerator gen, Object value, JavaType rootType)333 public void serializeValue(JsonGenerator gen, Object value, JavaType rootType) throws IOException 334 { 335 _generator = gen; 336 if (value == null) { 337 _serializeNull(gen); 338 return; 339 } 340 // Let's ensure types are compatible at this point 341 if (!rootType.getRawClass().isAssignableFrom(value.getClass())) { 342 _reportIncompatibleRootType(value, rootType); 343 } 344 // root value, not reached via property: 345 JsonSerializer<Object> ser = findTypedValueSerializer(rootType, true, null); 346 PropertyName rootName = _config.getFullRootName(); 347 if (rootName == null) { // not explicitly specified 348 if (_config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE)) { 349 _serialize(gen, value, ser, _config.findRootName(rootType)); 350 return; 351 } 352 } else if (!rootName.isEmpty()) { 353 _serialize(gen, value, ser, rootName); 354 return; 355 } 356 _serialize(gen, value, ser); 357 } 358 359 /** 360 * The method to be called by {@link ObjectWriter} 361 * for serializing given value (assumed to be of specified root type, 362 * instead of runtime type of value), when it may know specific 363 * {@link JsonSerializer} to use. 364 * 365 * @param rootType Type to use for locating serializer to use, instead of actual 366 * runtime type, if no serializer is passed 367 * @param ser Root Serializer to use, if not null 368 * 369 * @since 2.1 370 */ serializeValue(JsonGenerator gen, Object value, JavaType rootType, JsonSerializer<Object> ser)371 public void serializeValue(JsonGenerator gen, Object value, JavaType rootType, 372 JsonSerializer<Object> ser) throws IOException 373 { 374 _generator = gen; 375 if (value == null) { 376 _serializeNull(gen); 377 return; 378 } 379 // Let's ensure types are compatible at this point 380 if ((rootType != null) && !rootType.getRawClass().isAssignableFrom(value.getClass())) { 381 _reportIncompatibleRootType(value, rootType); 382 } 383 // root value, not reached via property: 384 if (ser == null) { 385 ser = findTypedValueSerializer(rootType, true, null); 386 } 387 PropertyName rootName = _config.getFullRootName(); 388 if (rootName == null) { // not explicitly specified 389 if (_config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE)) { 390 rootName = (rootType == null) 391 ? _config.findRootName(value.getClass()) 392 : _config.findRootName(rootType); 393 _serialize(gen, value, ser, rootName); 394 return; 395 } 396 } else if (!rootName.isEmpty()) { 397 _serialize(gen, value, ser, rootName); 398 return; 399 } 400 _serialize(gen, value, ser); 401 } 402 403 /** 404 * Alternate serialization call used for polymorphic types, when {@link TypeSerializer} 405 * is already known, but the actual serializer may or may not be. 406 * 407 * @since 2.6 408 */ serializePolymorphic(JsonGenerator gen, Object value, JavaType rootType, JsonSerializer<Object> valueSer, TypeSerializer typeSer)409 public void serializePolymorphic(JsonGenerator gen, Object value, JavaType rootType, 410 JsonSerializer<Object> valueSer, TypeSerializer typeSer) 411 throws IOException 412 { 413 _generator = gen; 414 if (value == null) { 415 _serializeNull(gen); 416 return; 417 } 418 // Let's ensure types are compatible at this point 419 if ((rootType != null) && !rootType.getRawClass().isAssignableFrom(value.getClass())) { 420 _reportIncompatibleRootType(value, rootType); 421 } 422 /* 12-Jun-2015, tatu: nominal root type is necessary for Maps at least; 423 * possibly collections, but can cause problems for other polymorphic 424 * types. We really need to distinguish between serialization type, 425 * base type; but right we don't. Hence this check 426 */ 427 if (valueSer == null) { 428 if ((rootType != null) && rootType.isContainerType()) { 429 valueSer = findValueSerializer(rootType, null); 430 } else { 431 valueSer = findValueSerializer(value.getClass(), null); 432 } 433 } 434 435 final boolean wrap; 436 PropertyName rootName = _config.getFullRootName(); 437 if (rootName == null) { 438 wrap = _config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE); 439 if (wrap) { 440 gen.writeStartObject(); 441 PropertyName pname = _config.findRootName(value.getClass()); 442 gen.writeFieldName(pname.simpleAsEncoded(_config)); 443 } 444 } else if (rootName.isEmpty()) { 445 wrap = false; 446 } else { 447 wrap = true; 448 gen.writeStartObject(); 449 gen.writeFieldName(rootName.getSimpleName()); 450 } 451 try { 452 valueSer.serializeWithType(value, gen, this, typeSer); 453 if (wrap) { 454 gen.writeEndObject(); 455 } 456 } catch (Exception e) { 457 throw _wrapAsIOE(gen, e); 458 } 459 } 460 _serialize(JsonGenerator gen, Object value, JsonSerializer<Object> ser, PropertyName rootName)461 private final void _serialize(JsonGenerator gen, Object value, 462 JsonSerializer<Object> ser, PropertyName rootName) 463 throws IOException 464 { 465 try { 466 gen.writeStartObject(); 467 gen.writeFieldName(rootName.simpleAsEncoded(_config)); 468 ser.serialize(value, gen, this); 469 gen.writeEndObject(); 470 } catch (Exception e) { 471 throw _wrapAsIOE(gen, e); 472 } 473 } 474 _serialize(JsonGenerator gen, Object value, JsonSerializer<Object> ser)475 private final void _serialize(JsonGenerator gen, Object value, 476 JsonSerializer<Object> ser) 477 throws IOException 478 { 479 try { 480 ser.serialize(value, gen, this); 481 } catch (Exception e) { 482 throw _wrapAsIOE(gen, e); 483 } 484 } 485 486 /** 487 * Helper method called when root value to serialize is null 488 * 489 * @since 2.3 490 */ _serializeNull(JsonGenerator gen)491 protected void _serializeNull(JsonGenerator gen) throws IOException 492 { 493 JsonSerializer<Object> ser = getDefaultNullValueSerializer(); 494 try { 495 ser.serialize(null, gen, this); 496 } catch (Exception e) { 497 throw _wrapAsIOE(gen, e); 498 } 499 } 500 _wrapAsIOE(JsonGenerator g, Exception e)501 private IOException _wrapAsIOE(JsonGenerator g, Exception e) { 502 if (e instanceof IOException) { 503 return (IOException) e; 504 } 505 String msg = ClassUtil.exceptionMessage(e); 506 if (msg == null) { 507 msg = "[no message for "+e.getClass().getName()+"]"; 508 } 509 return new JsonMappingException(g, msg, e); 510 } 511 512 /* 513 /******************************************************** 514 /* Access to caching details 515 /******************************************************** 516 */ 517 518 /** 519 * Method that can be used to determine how many serializers this 520 * provider is caching currently 521 * (if it does caching: default implementation does) 522 * Exact count depends on what kind of serializers get cached; 523 * default implementation caches all serializers, including ones that 524 * are eagerly constructed (for optimal access speed) 525 *<p> 526 * The main use case for this method is to allow conditional flushing of 527 * serializer cache, if certain number of entries is reached. 528 */ cachedSerializersCount()529 public int cachedSerializersCount() { 530 return _serializerCache.size(); 531 } 532 533 /** 534 * Method that will drop all serializers currently cached by this provider. 535 * This can be used to remove memory usage (in case some serializers are 536 * only used once or so), or to force re-construction of serializers after 537 * configuration changes for mapper than owns the provider. 538 */ flushCachedSerializers()539 public void flushCachedSerializers() { 540 _serializerCache.flush(); 541 } 542 543 /* 544 /********************************************************** 545 /* Extended API called by ObjectMapper: other 546 /********************************************************** 547 */ 548 549 /** 550 * The method to be called by {@link ObjectMapper} and {@link ObjectWriter} 551 * to to expose the format of the given to to the given visitor 552 * 553 * @param javaType The type for which to generate format 554 * @param visitor the visitor to accept the format 555 */ acceptJsonFormatVisitor(JavaType javaType, JsonFormatVisitorWrapper visitor)556 public void acceptJsonFormatVisitor(JavaType javaType, JsonFormatVisitorWrapper visitor) 557 throws JsonMappingException 558 { 559 if (javaType == null) { 560 throw new IllegalArgumentException("A class must be provided"); 561 } 562 /* no need for embedded type information for JSON schema generation (all 563 * type information it needs is accessible via "untyped" serializer) 564 */ 565 visitor.setProvider(this); 566 findValueSerializer(javaType, null).acceptJsonFormatVisitor(visitor, javaType); 567 } 568 569 /** 570 * The method to be called by {@link ObjectMapper} 571 * to generate <a href="http://json-schema.org/">JSON schema</a> for 572 * given type. 573 * 574 * @param type The type for which to generate schema 575 * 576 * @deprecated Should not be used any more 577 */ 578 @Deprecated // since 2.6 generateJsonSchema(Class<?> type)579 public com.fasterxml.jackson.databind.jsonschema.JsonSchema generateJsonSchema(Class<?> type) 580 throws JsonMappingException 581 { 582 /* no need for embedded type information for JSON schema generation (all 583 * type information it needs is accessible via "untyped" serializer) 584 */ 585 JsonSerializer<Object> ser = findValueSerializer(type, null); 586 JsonNode schemaNode = (ser instanceof SchemaAware) ? 587 ((SchemaAware) ser).getSchema(this, null) : com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode(); 588 if (!(schemaNode instanceof ObjectNode)) { 589 throw new IllegalArgumentException("Class " + type.getName() 590 +" would not be serialized as a JSON object and therefore has no schema"); 591 } 592 return new com.fasterxml.jackson.databind.jsonschema.JsonSchema((ObjectNode) schemaNode); 593 } 594 595 596 /* 597 /********************************************************** 598 /* Helper classes 599 /********************************************************** 600 */ 601 602 /** 603 * Concrete implementation that defines factory method(s), 604 * defined as final. 605 */ 606 public final static class Impl extends DefaultSerializerProvider { 607 private static final long serialVersionUID = 1L; 608 Impl()609 public Impl() { super(); } Impl(Impl src)610 public Impl(Impl src) { super(src); } 611 Impl(SerializerProvider src, SerializationConfig config, SerializerFactory f)612 protected Impl(SerializerProvider src, SerializationConfig config, 613 SerializerFactory f) { 614 super(src, config, f); 615 } 616 617 @Override copy()618 public DefaultSerializerProvider copy() 619 { 620 if (getClass() != Impl.class) { 621 return super.copy(); 622 } 623 return new Impl(this); 624 } 625 626 @Override createInstance(SerializationConfig config, SerializerFactory jsf)627 public Impl createInstance(SerializationConfig config, SerializerFactory jsf) { 628 return new Impl(this, config, jsf); 629 } 630 } 631 } 632