| package com.fasterxml.jackson.databind.ser; |
| |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| import java.net.InetAddress; |
| import java.net.InetSocketAddress; |
| import java.nio.ByteBuffer; |
| import java.util.*; |
| import java.util.concurrent.atomic.AtomicReference; |
| |
| import com.fasterxml.jackson.annotation.JsonFormat; |
| import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| import com.fasterxml.jackson.annotation.JsonInclude; |
| import com.fasterxml.jackson.databind.*; |
| import com.fasterxml.jackson.databind.annotation.JsonSerialize; |
| import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig; |
| import com.fasterxml.jackson.databind.ext.OptionalHandlerFactory; |
| import com.fasterxml.jackson.databind.introspect.*; |
| import com.fasterxml.jackson.databind.jsontype.NamedType; |
| import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; |
| import com.fasterxml.jackson.databind.jsontype.TypeSerializer; |
| import com.fasterxml.jackson.databind.ser.impl.*; |
| import com.fasterxml.jackson.databind.ser.std.*; |
| import com.fasterxml.jackson.databind.type.*; |
| import com.fasterxml.jackson.databind.util.*; |
| |
| /** |
| * Factory class that can provide serializers for standard JDK classes, |
| * as well as custom classes that extend standard classes or implement |
| * one of "well-known" interfaces (such as {@link java.util.Collection}). |
| *<p> |
| * Since all the serializers are eagerly instantiated, and there is |
| * no additional introspection or customizability of these types, |
| * this factory is essentially stateless. |
| */ |
| @SuppressWarnings("serial") |
| public abstract class BasicSerializerFactory |
| extends SerializerFactory |
| implements java.io.Serializable |
| { |
| /* |
| /********************************************************** |
| /* Configuration, lookup tables/maps |
| /********************************************************** |
| */ |
| |
| /** |
| * Since these are all JDK classes, we shouldn't have to worry |
| * about ClassLoader used to load them. Rather, we can just |
| * use the class name, and keep things simple and efficient. |
| */ |
| protected final static HashMap<String, JsonSerializer<?>> _concrete; |
| |
| /** |
| * Actually it may not make much sense to eagerly instantiate all |
| * kinds of serializers: so this Map actually contains class references, |
| * not instances |
| */ |
| protected final static HashMap<String, Class<? extends JsonSerializer<?>>> _concreteLazy; |
| |
| static { |
| HashMap<String, Class<? extends JsonSerializer<?>>> concLazy |
| = new HashMap<String, Class<? extends JsonSerializer<?>>>(); |
| HashMap<String, JsonSerializer<?>> concrete |
| = new HashMap<String, JsonSerializer<?>>(); |
| |
| |
| /* String and string-like types (note: date types explicitly |
| * not included -- can use either textual or numeric serialization) |
| */ |
| concrete.put(String.class.getName(), new StringSerializer()); |
| final ToStringSerializer sls = ToStringSerializer.instance; |
| concrete.put(StringBuffer.class.getName(), sls); |
| concrete.put(StringBuilder.class.getName(), sls); |
| concrete.put(Character.class.getName(), sls); |
| concrete.put(Character.TYPE.getName(), sls); |
| |
| // Primitives/wrappers for primitives (primitives needed for Beans) |
| NumberSerializers.addAll(concrete); |
| concrete.put(Boolean.TYPE.getName(), new BooleanSerializer(true)); |
| concrete.put(Boolean.class.getName(), new BooleanSerializer(false)); |
| |
| // Other numbers, more complicated |
| concrete.put(BigInteger.class.getName(), new NumberSerializer(BigInteger.class)); |
| concrete.put(BigDecimal.class.getName(),new NumberSerializer(BigDecimal.class)); |
| |
| // Other discrete non-container types: |
| // First, Date/Time zoo: |
| concrete.put(Calendar.class.getName(), CalendarSerializer.instance); |
| concrete.put(java.util.Date.class.getName(), DateSerializer.instance); |
| |
| // And then other standard non-structured JDK types |
| for (Map.Entry<Class<?>,Object> en : StdJdkSerializers.all()) { |
| Object value = en.getValue(); |
| if (value instanceof JsonSerializer<?>) { |
| concrete.put(en.getKey().getName(), (JsonSerializer<?>) value); |
| } else { |
| @SuppressWarnings("unchecked") |
| Class<? extends JsonSerializer<?>> cls = (Class<? extends JsonSerializer<?>>) value; |
| concLazy.put(en.getKey().getName(), cls); |
| } |
| } |
| |
| // Jackson-specific type(s) |
| // (Q: can this ever be sub-classed?) |
| concLazy.put(TokenBuffer.class.getName(), TokenBufferSerializer.class); |
| |
| _concrete = concrete; |
| _concreteLazy = concLazy; |
| } |
| |
| /* |
| /********************************************************** |
| /* Configuration |
| /********************************************************** |
| */ |
| |
| /** |
| * Configuration settings for this factory; immutable instance (just like this |
| * factory), new version created via copy-constructor (fluent-style) |
| */ |
| protected final SerializerFactoryConfig _factoryConfig; |
| |
| /* |
| /********************************************************** |
| /* Life cycle |
| /********************************************************** |
| */ |
| |
| /** |
| * We will provide default constructor to allow sub-classing, |
| * but make it protected so that no non-singleton instances of |
| * the class will be instantiated. |
| */ |
| protected BasicSerializerFactory(SerializerFactoryConfig config) { |
| _factoryConfig = (config == null) ? new SerializerFactoryConfig() : config; |
| } |
| |
| /** |
| * Method for getting current {@link SerializerFactoryConfig}. |
| *<p> |
| * Note that since instances are immutable, you can NOT change settings |
| * by accessing an instance and calling methods: this will simply create |
| * new instance of config object. |
| */ |
| public SerializerFactoryConfig getFactoryConfig() { |
| return _factoryConfig; |
| } |
| |
| /** |
| * Method used for creating a new instance of this factory, but with different |
| * configuration. Reason for specifying factory method (instead of plain constructor) |
| * is to allow proper sub-classing of factories. |
| *<p> |
| * Note that custom sub-classes generally <b>must override</b> implementation |
| * of this method, as it usually requires instantiating a new instance of |
| * factory type. Check out javadocs for |
| * {@link com.fasterxml.jackson.databind.ser.BeanSerializerFactory} for more details. |
| */ |
| public abstract SerializerFactory withConfig(SerializerFactoryConfig config); |
| |
| /** |
| * Convenience method for creating a new factory instance with an additional |
| * serializer provider. |
| */ |
| @Override |
| public final SerializerFactory withAdditionalSerializers(Serializers additional) { |
| return withConfig(_factoryConfig.withAdditionalSerializers(additional)); |
| } |
| |
| /** |
| * Convenience method for creating a new factory instance with an additional |
| * key serializer provider. |
| */ |
| @Override |
| public final SerializerFactory withAdditionalKeySerializers(Serializers additional) { |
| return withConfig(_factoryConfig.withAdditionalKeySerializers(additional)); |
| } |
| |
| /** |
| * Convenience method for creating a new factory instance with additional bean |
| * serializer modifier. |
| */ |
| @Override |
| public final SerializerFactory withSerializerModifier(BeanSerializerModifier modifier) { |
| return withConfig(_factoryConfig.withSerializerModifier(modifier)); |
| } |
| |
| /* |
| /********************************************************** |
| /* SerializerFactory impl |
| /********************************************************** |
| */ |
| |
| // Implemented by sub-classes |
| @Override |
| public abstract JsonSerializer<Object> createSerializer(SerializerProvider prov, |
| JavaType type) |
| throws JsonMappingException; |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public JsonSerializer<Object> createKeySerializer(SerializationConfig config, |
| JavaType keyType, JsonSerializer<Object> defaultImpl) |
| { |
| // We should not need any member method info; at most class annotations for Map type |
| // ... at least, not here. |
| BeanDescription beanDesc = config.introspectClassAnnotations(keyType.getRawClass()); |
| JsonSerializer<?> ser = null; |
| // Minor optimization: to avoid constructing beanDesc, bail out if none registered |
| if (_factoryConfig.hasKeySerializers()) { |
| // Only thing we have here are module-provided key serializers: |
| for (Serializers serializers : _factoryConfig.keySerializers()) { |
| ser = serializers.findSerializer(config, keyType, beanDesc); |
| if (ser != null) { |
| break; |
| } |
| } |
| } |
| if (ser == null) { |
| ser = defaultImpl; |
| if (ser == null) { |
| ser = StdKeySerializers.getStdKeySerializer(config, keyType.getRawClass(), false); |
| // As per [databind#47], also need to support @JsonValue |
| if (ser == null) { |
| beanDesc = config.introspect(keyType); |
| AnnotatedMember am = beanDesc.findJsonValueAccessor(); |
| if (am != null) { |
| final Class<?> rawType = am.getRawType(); |
| JsonSerializer<?> delegate = StdKeySerializers.getStdKeySerializer(config, |
| rawType, true); |
| if (config.canOverrideAccessModifiers()) { |
| ClassUtil.checkAndFixAccess(am.getMember(), |
| config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); |
| } |
| ser = new JsonValueSerializer(am, delegate); |
| } else { |
| ser = StdKeySerializers.getFallbackKeySerializer(config, keyType.getRawClass()); |
| } |
| } |
| } |
| } |
| |
| // [databind#120]: Allow post-processing |
| if (_factoryConfig.hasSerializerModifiers()) { |
| for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { |
| ser = mod.modifyKeySerializer(config, keyType, beanDesc, ser); |
| } |
| } |
| return (JsonSerializer<Object>) ser; |
| } |
| |
| /** |
| * Method called to construct a type serializer for values with given declared |
| * base type. This is called for values other than those of bean property |
| * types. |
| */ |
| @Override |
| public TypeSerializer createTypeSerializer(SerializationConfig config, |
| JavaType baseType) |
| { |
| BeanDescription bean = config.introspectClassAnnotations(baseType.getRawClass()); |
| AnnotatedClass ac = bean.getClassInfo(); |
| AnnotationIntrospector ai = config.getAnnotationIntrospector(); |
| TypeResolverBuilder<?> b = ai.findTypeResolver(config, ac, baseType); |
| /* Ok: if there is no explicit type info handler, we may want to |
| * use a default. If so, config object knows what to use. |
| */ |
| Collection<NamedType> subtypes = null; |
| if (b == null) { |
| b = config.getDefaultTyper(baseType); |
| } else { |
| subtypes = config.getSubtypeResolver().collectAndResolveSubtypesByClass(config, ac); |
| } |
| if (b == null) { |
| return null; |
| } |
| // 10-Jun-2015, tatu: Since not created for Bean Property, no need for post-processing |
| // wrt EXTERNAL_PROPERTY |
| return b.buildTypeSerializer(config, baseType, subtypes); |
| } |
| |
| /* |
| /********************************************************** |
| /* Additional API for other core classes |
| /********************************************************** |
| */ |
| |
| protected abstract Iterable<Serializers> customSerializers(); |
| |
| /* |
| /********************************************************** |
| /* Overridable secondary serializer accessor methods |
| /********************************************************** |
| */ |
| |
| /** |
| * Method that will use fast lookup (and identity comparison) methods to |
| * see if we know serializer to use for given type. |
| */ |
| protected final JsonSerializer<?> findSerializerByLookup(JavaType type, |
| SerializationConfig config, BeanDescription beanDesc, |
| boolean staticTyping) |
| { |
| Class<?> raw = type.getRawClass(); |
| String clsName = raw.getName(); |
| JsonSerializer<?> ser = _concrete.get(clsName); |
| if (ser == null) { |
| Class<? extends JsonSerializer<?>> serClass = _concreteLazy.get(clsName); |
| if (serClass != null) { |
| // 07-Jan-2017, tatu: Should never fail (since we control constructors), |
| // but if it does will throw `IllegalArgumentException` with description, |
| // which we could catch, re-title. |
| return ClassUtil.createInstance(serClass, false); |
| } |
| } |
| return ser; |
| } |
| |
| /** |
| * Method called to see if one of primary per-class annotations |
| * (or related, like implementing of {@link JsonSerializable}) |
| * determines the serializer to use. |
| *<p> |
| * Currently handles things like: |
| *<ul> |
| * <li>If type implements {@link JsonSerializable}, use that |
| * </li> |
| * <li>If type has {@link com.fasterxml.jackson.annotation.JsonValue} annotation (or equivalent), build serializer |
| * based on that property |
| * </li> |
| *</ul> |
| * |
| * @since 2.0 |
| */ |
| protected final JsonSerializer<?> findSerializerByAnnotations(SerializerProvider prov, |
| JavaType type, BeanDescription beanDesc) |
| throws JsonMappingException |
| { |
| Class<?> raw = type.getRawClass(); |
| // First: JsonSerializable? |
| if (JsonSerializable.class.isAssignableFrom(raw)) { |
| return SerializableSerializer.instance; |
| } |
| // Second: @JsonValue for any type |
| AnnotatedMember valueAccessor = beanDesc.findJsonValueAccessor(); |
| if (valueAccessor != null) { |
| if (prov.canOverrideAccessModifiers()) { |
| ClassUtil.checkAndFixAccess(valueAccessor.getMember(), |
| prov.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); |
| } |
| JsonSerializer<Object> ser = findSerializerFromAnnotation(prov, valueAccessor); |
| return new JsonValueSerializer(valueAccessor, ser); |
| } |
| // No well-known annotations... |
| return null; |
| } |
| |
| /** |
| * Method for checking if we can determine serializer to use based on set of |
| * known primary types, checking for set of known base types (exact matches |
| * having been compared against with <code>findSerializerByLookup</code>). |
| * This does not include "secondary" interfaces, but |
| * mostly concrete or abstract base classes. |
| */ |
| protected final JsonSerializer<?> findSerializerByPrimaryType(SerializerProvider prov, |
| JavaType type, BeanDescription beanDesc, |
| boolean staticTyping) |
| throws JsonMappingException |
| { |
| Class<?> raw = type.getRawClass(); |
| |
| // Then check for optional/external serializers |
| JsonSerializer<?> ser = findOptionalStdSerializer(prov, type, beanDesc, staticTyping); |
| if (ser != null) { |
| return ser; |
| } |
| |
| if (Calendar.class.isAssignableFrom(raw)) { |
| return CalendarSerializer.instance; |
| } |
| if (java.util.Date.class.isAssignableFrom(raw)) { |
| return DateSerializer.instance; |
| } |
| if (Map.Entry.class.isAssignableFrom(raw)) { |
| // 18-Oct-2015, tatu: With 2.7, need to dig type info: |
| JavaType mapEntryType = type.findSuperType(Map.Entry.class); |
| |
| // 28-Apr-2015, tatu: TypeFactory does it all for us already so |
| JavaType kt = mapEntryType.containedTypeOrUnknown(0); |
| JavaType vt = mapEntryType.containedTypeOrUnknown(1); |
| return buildMapEntrySerializer(prov, type, beanDesc, staticTyping, kt, vt); |
| } |
| if (ByteBuffer.class.isAssignableFrom(raw)) { |
| return new ByteBufferSerializer(); |
| } |
| if (InetAddress.class.isAssignableFrom(raw)) { |
| return new InetAddressSerializer(); |
| } |
| if (InetSocketAddress.class.isAssignableFrom(raw)) { |
| return new InetSocketAddressSerializer(); |
| } |
| if (TimeZone.class.isAssignableFrom(raw)) { |
| return new TimeZoneSerializer(); |
| } |
| if (java.nio.charset.Charset.class.isAssignableFrom(raw)) { |
| return ToStringSerializer.instance; |
| } |
| if (Number.class.isAssignableFrom(raw)) { |
| // 21-May-2014, tatu: Couple of alternatives actually |
| JsonFormat.Value format = beanDesc.findExpectedFormat(null); |
| if (format != null) { |
| switch (format.getShape()) { |
| case STRING: |
| return ToStringSerializer.instance; |
| case OBJECT: // need to bail out to let it be serialized as POJO |
| case ARRAY: // or, I guess ARRAY; otherwise no point in speculating |
| return null; |
| default: |
| } |
| } |
| return NumberSerializer.instance; |
| } |
| if (ClassUtil.isEnumType(raw) && raw != Enum.class) { |
| return buildEnumSerializer(prov.getConfig(), type, beanDesc); |
| } |
| return null; |
| } |
| |
| /** |
| * Overridable method called after checking all other types. |
| * |
| * @since 2.2 |
| */ |
| protected JsonSerializer<?> findOptionalStdSerializer(SerializerProvider prov, |
| JavaType type, BeanDescription beanDesc, boolean staticTyping) |
| throws JsonMappingException |
| { |
| return OptionalHandlerFactory.instance.findSerializer(prov.getConfig(), type, beanDesc); |
| } |
| |
| /** |
| * Reflection-based serialized find method, which checks if |
| * given class implements one of recognized "add-on" interfaces. |
| * Add-on here means a role that is usually or can be a secondary |
| * trait: for example, |
| * bean classes may implement {@link Iterable}, but their main |
| * function is usually something else. The reason for |
| */ |
| protected final JsonSerializer<?> findSerializerByAddonType(SerializationConfig config, |
| JavaType javaType, BeanDescription beanDesc, boolean staticTyping) throws JsonMappingException |
| { |
| Class<?> rawType = javaType.getRawClass(); |
| |
| if (Iterator.class.isAssignableFrom(rawType)) { |
| JavaType[] params = config.getTypeFactory().findTypeParameters(javaType, Iterator.class); |
| JavaType vt = (params == null || params.length != 1) ? |
| TypeFactory.unknownType() : params[0]; |
| return buildIteratorSerializer(config, javaType, beanDesc, staticTyping, vt); |
| } |
| if (Iterable.class.isAssignableFrom(rawType)) { |
| JavaType[] params = config.getTypeFactory().findTypeParameters(javaType, Iterable.class); |
| JavaType vt = (params == null || params.length != 1) ? |
| TypeFactory.unknownType() : params[0]; |
| return buildIterableSerializer(config, javaType, beanDesc, staticTyping, vt); |
| } |
| if (CharSequence.class.isAssignableFrom(rawType)) { |
| return ToStringSerializer.instance; |
| } |
| return null; |
| } |
| |
| /** |
| * Helper method called to check if a class or method |
| * has an annotation |
| * (@link com.fasterxml.jackson.databind.annotation.JsonSerialize#using) |
| * that tells the class to use for serialization. |
| * Returns null if no such annotation found. |
| */ |
| @SuppressWarnings("unchecked") |
| protected JsonSerializer<Object> findSerializerFromAnnotation(SerializerProvider prov, |
| Annotated a) |
| throws JsonMappingException |
| { |
| Object serDef = prov.getAnnotationIntrospector().findSerializer(a); |
| if (serDef == null) { |
| return null; |
| } |
| JsonSerializer<Object> ser = prov.serializerInstance(a, serDef); |
| // One more thing however: may need to also apply a converter: |
| return (JsonSerializer<Object>) findConvertingSerializer(prov, a, ser); |
| } |
| |
| /** |
| * Helper method that will check whether given annotated entity (usually class, |
| * but may also be a property accessor) indicates that a {@link Converter} is to |
| * be used; and if so, to construct and return suitable serializer for it. |
| * If not, will simply return given serializer as is. |
| */ |
| protected JsonSerializer<?> findConvertingSerializer(SerializerProvider prov, |
| Annotated a, JsonSerializer<?> ser) |
| throws JsonMappingException |
| { |
| Converter<Object,Object> conv = findConverter(prov, a); |
| if (conv == null) { |
| return ser; |
| } |
| JavaType delegateType = conv.getOutputType(prov.getTypeFactory()); |
| return new StdDelegatingSerializer(conv, delegateType, ser); |
| } |
| |
| protected Converter<Object,Object> findConverter(SerializerProvider prov, |
| Annotated a) |
| throws JsonMappingException |
| { |
| Object convDef = prov.getAnnotationIntrospector().findSerializationConverter(a); |
| if (convDef == null) { |
| return null; |
| } |
| return prov.converterInstance(a, convDef); |
| } |
| |
| /* |
| /********************************************************** |
| /* Factory methods, container types: |
| /********************************************************** |
| */ |
| |
| /** |
| * @since 2.1 |
| */ |
| protected JsonSerializer<?> buildContainerSerializer(SerializerProvider prov, |
| JavaType type, BeanDescription beanDesc, boolean staticTyping) |
| throws JsonMappingException |
| { |
| final SerializationConfig config = prov.getConfig(); |
| |
| /* [databind#23], 15-Mar-2013, tatu: must force static handling of root value type, |
| * with just one important exception: if value type is "untyped", let's |
| * leave it as is; no clean way to make it work. |
| */ |
| if (!staticTyping && type.useStaticType()) { |
| if (!type.isContainerType() || !type.getContentType().isJavaLangObject()) { |
| staticTyping = true; |
| } |
| } |
| |
| // Let's see what we can learn about element/content/value type, type serializer for it: |
| JavaType elementType = type.getContentType(); |
| TypeSerializer elementTypeSerializer = createTypeSerializer(config, |
| elementType); |
| |
| // if elements have type serializer, cannot force static typing: |
| if (elementTypeSerializer != null) { |
| staticTyping = false; |
| } |
| JsonSerializer<Object> elementValueSerializer = _findContentSerializer(prov, |
| beanDesc.getClassInfo()); |
| if (type.isMapLikeType()) { // implements java.util.Map |
| MapLikeType mlt = (MapLikeType) type; |
| /* 29-Sep-2012, tatu: This is actually too early to (try to) find |
| * key serializer from property annotations, and can lead to caching |
| * issues (see [databind#75]). Instead, must be done from 'createContextual()' call. |
| * But we do need to check class annotations. |
| */ |
| JsonSerializer<Object> keySerializer = _findKeySerializer(prov, beanDesc.getClassInfo()); |
| if (mlt.isTrueMapType()) { |
| return buildMapSerializer(prov, (MapType) mlt, beanDesc, staticTyping, |
| keySerializer, elementTypeSerializer, elementValueSerializer); |
| } |
| // With Map-like, just 2 options: (1) Custom, (2) Annotations |
| JsonSerializer<?> ser = null; |
| MapLikeType mlType = (MapLikeType) type; |
| for (Serializers serializers : customSerializers()) { // (1) Custom |
| ser = serializers.findMapLikeSerializer(config, |
| mlType, beanDesc, keySerializer, elementTypeSerializer, elementValueSerializer); |
| if (ser != null) { |
| break; |
| } |
| } |
| if (ser == null) { // (2) Annotations-based ones: |
| ser = findSerializerByAnnotations(prov, type, beanDesc); |
| } |
| if (ser != null) { |
| if (_factoryConfig.hasSerializerModifiers()) { |
| for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { |
| ser = mod.modifyMapLikeSerializer(config, mlType, beanDesc, ser); |
| } |
| } |
| } |
| return ser; |
| } |
| if (type.isCollectionLikeType()) { |
| CollectionLikeType clt = (CollectionLikeType) type; |
| if (clt.isTrueCollectionType()) { |
| return buildCollectionSerializer(prov, (CollectionType) clt, beanDesc, staticTyping, |
| elementTypeSerializer, elementValueSerializer); |
| } |
| // With Map-like, just 2 options: (1) Custom, (2) Annotations |
| JsonSerializer<?> ser = null; |
| CollectionLikeType clType = (CollectionLikeType) type; |
| for (Serializers serializers : customSerializers()) { // (1) Custom |
| ser = serializers.findCollectionLikeSerializer(config, |
| clType, beanDesc, elementTypeSerializer, elementValueSerializer); |
| if (ser != null) { |
| break; |
| } |
| } |
| if (ser == null) { // (2) Annotations-based ones: |
| ser = findSerializerByAnnotations(prov, type, beanDesc); |
| } |
| if (ser != null) { |
| if (_factoryConfig.hasSerializerModifiers()) { |
| for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { |
| ser = mod.modifyCollectionLikeSerializer(config, clType, beanDesc, ser); |
| } |
| } |
| } |
| return ser; |
| } |
| if (type.isArrayType()) { |
| return buildArraySerializer(prov, (ArrayType) type, beanDesc, staticTyping, |
| elementTypeSerializer, elementValueSerializer); |
| } |
| return null; |
| } |
| |
| /** |
| * Helper method that handles configuration details when constructing serializers for |
| * {@link java.util.List} types that support efficient by-index access |
| * |
| * @since 2.1 |
| */ |
| protected JsonSerializer<?> buildCollectionSerializer(SerializerProvider prov, |
| CollectionType type, BeanDescription beanDesc, boolean staticTyping, |
| TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer) |
| throws JsonMappingException |
| { |
| SerializationConfig config = prov.getConfig(); |
| JsonSerializer<?> ser = null; |
| // Order of lookups: |
| // 1. Custom serializers |
| // 2. Annotations (@JsonValue, @JsonDeserialize) |
| // 3. Defaults |
| for (Serializers serializers : customSerializers()) { // (1) Custom |
| ser = serializers.findCollectionSerializer(config, |
| type, beanDesc, elementTypeSerializer, elementValueSerializer); |
| if (ser != null) { |
| break; |
| } |
| } |
| |
| if (ser == null) { |
| ser = findSerializerByAnnotations(prov, type, beanDesc); // (2) Annotations |
| if (ser == null) { |
| // We may also want to use serialize Collections "as beans", if (and only if) |
| // this is specified with `@JsonFormat(shape=Object)` |
| JsonFormat.Value format = beanDesc.findExpectedFormat(null); |
| if ((format != null) && format.getShape() == JsonFormat.Shape.OBJECT) { |
| return null; |
| } |
| Class<?> raw = type.getRawClass(); |
| if (EnumSet.class.isAssignableFrom(raw)) { |
| // this may or may not be available (Class doesn't; type of field/method does) |
| JavaType enumType = type.getContentType(); |
| // and even if nominally there is something, only use if it really is enum |
| if (!enumType.isEnumType()) { |
| enumType = null; |
| } |
| ser = buildEnumSetSerializer(enumType); |
| } else { |
| Class<?> elementRaw = type.getContentType().getRawClass(); |
| if (isIndexedList(raw)) { |
| if (elementRaw == String.class) { |
| // [JACKSON-829] Must NOT use if we have custom serializer |
| if (ClassUtil.isJacksonStdImpl(elementValueSerializer)) { |
| ser = IndexedStringListSerializer.instance; |
| } |
| } else { |
| ser = buildIndexedListSerializer(type.getContentType(), staticTyping, |
| elementTypeSerializer, elementValueSerializer); |
| } |
| } else if (elementRaw == String.class) { |
| // [JACKSON-829] Must NOT use if we have custom serializer |
| if (ClassUtil.isJacksonStdImpl(elementValueSerializer)) { |
| ser = StringCollectionSerializer.instance; |
| } |
| } |
| if (ser == null) { |
| ser = buildCollectionSerializer(type.getContentType(), staticTyping, |
| elementTypeSerializer, elementValueSerializer); |
| } |
| } |
| } |
| } |
| // [databind#120]: Allow post-processing |
| if (_factoryConfig.hasSerializerModifiers()) { |
| for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { |
| ser = mod.modifyCollectionSerializer(config, type, beanDesc, ser); |
| } |
| } |
| return ser; |
| } |
| |
| /* |
| /********************************************************** |
| /* Factory methods, for Collections |
| /********************************************************** |
| */ |
| |
| protected boolean isIndexedList(Class<?> cls) |
| { |
| return RandomAccess.class.isAssignableFrom(cls); |
| } |
| |
| public ContainerSerializer<?> buildIndexedListSerializer(JavaType elemType, |
| boolean staticTyping, TypeSerializer vts, JsonSerializer<Object> valueSerializer) { |
| return new IndexedListSerializer(elemType, staticTyping, vts, valueSerializer); |
| } |
| |
| public ContainerSerializer<?> buildCollectionSerializer(JavaType elemType, |
| boolean staticTyping, TypeSerializer vts, JsonSerializer<Object> valueSerializer) { |
| return new CollectionSerializer(elemType, staticTyping, vts, valueSerializer); |
| } |
| |
| public JsonSerializer<?> buildEnumSetSerializer(JavaType enumType) { |
| return new EnumSetSerializer(enumType); |
| } |
| |
| /* |
| /********************************************************** |
| /* Factory methods, for Maps |
| /********************************************************** |
| */ |
| |
| /** |
| * Helper method that handles configuration details when constructing serializers for |
| * {@link java.util.Map} types. |
| */ |
| protected JsonSerializer<?> buildMapSerializer(SerializerProvider prov, |
| MapType type, BeanDescription beanDesc, |
| boolean staticTyping, JsonSerializer<Object> keySerializer, |
| TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer) |
| throws JsonMappingException |
| { |
| // [databind#467]: This is where we could allow serialization "as POJO": But! It's |
| // nasty to undo, and does not apply on per-property basis. So, hardly optimal |
| JsonFormat.Value format = beanDesc.findExpectedFormat(null); |
| if ((format != null) && format.getShape() == JsonFormat.Shape.OBJECT) { |
| return null; |
| } |
| |
| JsonSerializer<?> ser = null; |
| |
| // Order of lookups: |
| // 1. Custom serializers |
| // 2. Annotations (@JsonValue, @JsonDeserialize) |
| // 3. Defaults |
| |
| final SerializationConfig config = prov.getConfig(); |
| for (Serializers serializers : customSerializers()) { // (1) Custom |
| ser = serializers.findMapSerializer(config, type, beanDesc, |
| keySerializer, elementTypeSerializer, elementValueSerializer); |
| if (ser != null) { break; } |
| } |
| if (ser == null) { |
| ser = findSerializerByAnnotations(prov, type, beanDesc); // (2) Annotations |
| if (ser == null) { |
| Object filterId = findFilterId(config, beanDesc); |
| // 01-May-2016, tatu: Which base type to use here gets tricky, since |
| // most often it ought to be `Map` or `EnumMap`, but due to abstract |
| // mapping it will more likely be concrete type like `HashMap`. |
| // So, for time being, just pass `Map.class` |
| JsonIgnoreProperties.Value ignorals = config.getDefaultPropertyIgnorals(Map.class, |
| beanDesc.getClassInfo()); |
| Set<String> ignored = (ignorals == null) ? null |
| : ignorals.findIgnoredForSerialization(); |
| MapSerializer mapSer = MapSerializer.construct(ignored, |
| type, staticTyping, elementTypeSerializer, |
| keySerializer, elementValueSerializer, filterId); |
| ser = _checkMapContentInclusion(prov, beanDesc, mapSer); |
| } |
| } |
| // [databind#120]: Allow post-processing |
| if (_factoryConfig.hasSerializerModifiers()) { |
| for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { |
| ser = mod.modifyMapSerializer(config, type, beanDesc, ser); |
| } |
| } |
| return ser; |
| } |
| |
| /** |
| * Helper method that does figures out content inclusion value to use, if any, |
| * and construct re-configured {@link MapSerializer} appropriately. |
| * |
| * @since 2.9 |
| */ |
| @SuppressWarnings("deprecation") |
| protected MapSerializer _checkMapContentInclusion(SerializerProvider prov, |
| BeanDescription beanDesc, MapSerializer mapSer) |
| throws JsonMappingException |
| { |
| final JavaType contentType = mapSer.getContentType(); |
| JsonInclude.Value inclV = _findInclusionWithContent(prov, beanDesc, |
| contentType, Map.class); |
| |
| // Need to support global legacy setting, for now: |
| JsonInclude.Include incl = (inclV == null) ? JsonInclude.Include.USE_DEFAULTS : inclV.getContentInclusion(); |
| if (incl == JsonInclude.Include.USE_DEFAULTS |
| || incl == JsonInclude.Include.ALWAYS) { |
| if (!prov.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES)) { |
| return mapSer.withContentInclusion(null, true); |
| } |
| return mapSer; |
| } |
| |
| // NOTE: mostly copied from `PropertyBuilder`; would be nice to refactor |
| // but code is not identical nor are these types related |
| Object valueToSuppress; |
| boolean suppressNulls = true; // almost always, but possibly not with CUSTOM |
| |
| switch (incl) { |
| case NON_DEFAULT: |
| valueToSuppress = BeanUtil.getDefaultValue(contentType); |
| if (valueToSuppress != null) { |
| if (valueToSuppress.getClass().isArray()) { |
| valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress); |
| } |
| } |
| break; |
| case NON_ABSENT: // new with 2.6, to support Guava/JDK8 Optionals |
| // and for referential types, also "empty", which in their case means "absent" |
| valueToSuppress = contentType.isReferenceType() |
| ? MapSerializer.MARKER_FOR_EMPTY : null; |
| break; |
| case NON_EMPTY: |
| valueToSuppress = MapSerializer.MARKER_FOR_EMPTY; |
| break; |
| case CUSTOM: // new with 2.9 |
| valueToSuppress = prov.includeFilterInstance(null, inclV.getContentFilter()); |
| if (valueToSuppress == null) { // is this legal? |
| suppressNulls = true; |
| } else { |
| suppressNulls = prov.includeFilterSuppressNulls(valueToSuppress); |
| } |
| break; |
| case NON_NULL: |
| default: // should not matter but... |
| valueToSuppress = null; |
| break; |
| } |
| return mapSer.withContentInclusion(valueToSuppress, suppressNulls); |
| } |
| |
| /** |
| * @since 2.9 |
| */ |
| protected JsonSerializer<?> buildMapEntrySerializer(SerializerProvider prov, |
| JavaType type, BeanDescription beanDesc, boolean staticTyping, |
| JavaType keyType, JavaType valueType) |
| throws JsonMappingException |
| { |
| // [databind#865]: Allow serialization "as POJO" -- note: to undo, declare |
| // serialization as `Shape.NATURAL` instead; that's JSON Object too. |
| JsonFormat.Value formatOverride = prov.getDefaultPropertyFormat(Map.Entry.class); |
| JsonFormat.Value formatFromAnnotation = beanDesc.findExpectedFormat(null); |
| JsonFormat.Value format = JsonFormat.Value.merge(formatFromAnnotation, formatOverride); |
| if (format.getShape() == JsonFormat.Shape.OBJECT) { |
| return null; |
| } |
| MapEntrySerializer ser = new MapEntrySerializer(valueType, keyType, valueType, |
| staticTyping, createTypeSerializer(prov.getConfig(), valueType), null); |
| |
| final JavaType contentType = ser.getContentType(); |
| JsonInclude.Value inclV = _findInclusionWithContent(prov, beanDesc, |
| contentType, Map.Entry.class); |
| |
| // Need to support global legacy setting, for now: |
| JsonInclude.Include incl = (inclV == null) ? JsonInclude.Include.USE_DEFAULTS : inclV.getContentInclusion(); |
| if (incl == JsonInclude.Include.USE_DEFAULTS |
| || incl == JsonInclude.Include.ALWAYS) { |
| return ser; |
| } |
| |
| // NOTE: mostly copied from `PropertyBuilder`; would be nice to refactor |
| // but code is not identical nor are these types related |
| Object valueToSuppress; |
| boolean suppressNulls = true; // almost always, but possibly not with CUSTOM |
| |
| switch (incl) { |
| case NON_DEFAULT: |
| valueToSuppress = BeanUtil.getDefaultValue(contentType); |
| if (valueToSuppress != null) { |
| if (valueToSuppress.getClass().isArray()) { |
| valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress); |
| } |
| } |
| break; |
| case NON_ABSENT: |
| valueToSuppress = contentType.isReferenceType() |
| ? MapSerializer.MARKER_FOR_EMPTY : null; |
| break; |
| case NON_EMPTY: |
| valueToSuppress = MapSerializer.MARKER_FOR_EMPTY; |
| break; |
| case CUSTOM: |
| valueToSuppress = prov.includeFilterInstance(null, inclV.getContentFilter()); |
| if (valueToSuppress == null) { // is this legal? |
| suppressNulls = true; |
| } else { |
| suppressNulls = prov.includeFilterSuppressNulls(valueToSuppress); |
| } |
| break; |
| case NON_NULL: |
| default: // should not matter but... |
| valueToSuppress = null; |
| break; |
| } |
| return ser.withContentInclusion(valueToSuppress, suppressNulls); |
| } |
| |
| /** |
| * Helper method used for finding inclusion definitions for structured |
| * container types like <code>Map</code>s and referential types |
| * (like <code>AtomicReference</code>). |
| * |
| * @param contentType Declared full content type of container |
| * @param configType Raw base type under which `configOverride`, if any, needs to be defined |
| */ |
| protected JsonInclude.Value _findInclusionWithContent(SerializerProvider prov, |
| BeanDescription beanDesc, |
| JavaType contentType, Class<?> configType) |
| throws JsonMappingException |
| { |
| final SerializationConfig config = prov.getConfig(); |
| |
| // Defaulting gets complicated because we might have two distinct |
| // axis to consider: Container type itself , and then value (content) type. |
| // Start with Container-defaults, then use more-specific value override, if any. |
| |
| // Start by getting global setting, overridden by Map-type-override |
| JsonInclude.Value inclV = beanDesc.findPropertyInclusion(config.getDefaultPropertyInclusion()); |
| inclV = config.getDefaultPropertyInclusion(configType, inclV); |
| |
| // and then merge content-type overrides, if any. But note that there's |
| // content-to-value inclusion shift we have to do |
| JsonInclude.Value valueIncl = config.getDefaultPropertyInclusion(contentType.getRawClass(), null); |
| |
| if (valueIncl != null) { |
| switch (valueIncl.getValueInclusion()) { |
| case USE_DEFAULTS: |
| break; |
| case CUSTOM: |
| inclV = inclV.withContentFilter(valueIncl.getContentFilter()); |
| break; |
| default: |
| inclV = inclV.withContentInclusion(valueIncl.getValueInclusion()); |
| } |
| } |
| return inclV; |
| } |
| |
| /* |
| /********************************************************** |
| /* Factory methods, for Arrays |
| /********************************************************** |
| */ |
| |
| /** |
| * Helper method that handles configuration details when constructing serializers for |
| * <code>Object[]</code> (and subtypes, except for String). |
| */ |
| protected JsonSerializer<?> buildArraySerializer(SerializerProvider prov, |
| ArrayType type, BeanDescription beanDesc, |
| boolean staticTyping, |
| TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer) |
| throws JsonMappingException |
| { |
| // 25-Jun-2015, tatu: Note that unlike with Collection(Like) and Map(Like) types, array |
| // types cannot be annotated (in theory I guess we could have mix-ins but... ?) |
| // so we need not do primary annotation lookup here. |
| // So all we need is (1) Custom, (2) Default array serializers |
| SerializationConfig config = prov.getConfig(); |
| JsonSerializer<?> ser = null; |
| |
| for (Serializers serializers : customSerializers()) { // (1) Custom |
| ser = serializers.findArraySerializer(config, |
| type, beanDesc, elementTypeSerializer, elementValueSerializer); |
| if (ser != null) { |
| break; |
| } |
| } |
| |
| if (ser == null) { |
| Class<?> raw = type.getRawClass(); |
| // Important: do NOT use standard serializers if non-standard element value serializer specified |
| if (elementValueSerializer == null || ClassUtil.isJacksonStdImpl(elementValueSerializer)) { |
| if (String[].class == raw) { |
| ser = StringArraySerializer.instance; |
| } else { |
| // other standard types? |
| ser = StdArraySerializers.findStandardImpl(raw); |
| } |
| } |
| if (ser == null) { |
| ser = new ObjectArraySerializer(type.getContentType(), staticTyping, elementTypeSerializer, |
| elementValueSerializer); |
| } |
| } |
| // [databind#120]: Allow post-processing |
| if (_factoryConfig.hasSerializerModifiers()) { |
| for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { |
| ser = mod.modifyArraySerializer(config, type, beanDesc, ser); |
| } |
| } |
| return ser; |
| } |
| |
| /* |
| /********************************************************** |
| /* Factory methods for Reference types |
| /* (demoted from BeanSF down here in 2.9) |
| /********************************************************** |
| */ |
| |
| /** |
| * @since 2.7 |
| */ |
| public JsonSerializer<?> findReferenceSerializer(SerializerProvider prov, ReferenceType refType, |
| BeanDescription beanDesc, boolean staticTyping) |
| throws JsonMappingException |
| { |
| JavaType contentType = refType.getContentType(); |
| TypeSerializer contentTypeSerializer = contentType.getTypeHandler(); |
| final SerializationConfig config = prov.getConfig(); |
| if (contentTypeSerializer == null) { |
| contentTypeSerializer = createTypeSerializer(config, contentType); |
| } |
| JsonSerializer<Object> contentSerializer = contentType.getValueHandler(); |
| for (Serializers serializers : customSerializers()) { |
| JsonSerializer<?> ser = serializers.findReferenceSerializer(config, refType, beanDesc, |
| contentTypeSerializer, contentSerializer); |
| if (ser != null) { |
| return ser; |
| } |
| } |
| if (refType.isTypeOrSubTypeOf(AtomicReference.class)) { |
| return buildAtomicReferenceSerializer(prov, refType, beanDesc, staticTyping, |
| contentTypeSerializer, contentSerializer); |
| } |
| return null; |
| } |
| |
| protected JsonSerializer<?> buildAtomicReferenceSerializer(SerializerProvider prov, |
| ReferenceType refType, BeanDescription beanDesc, boolean staticTyping, |
| TypeSerializer contentTypeSerializer, JsonSerializer<Object> contentSerializer) |
| throws JsonMappingException |
| { |
| final JavaType contentType = refType.getReferencedType(); |
| JsonInclude.Value inclV = _findInclusionWithContent(prov, beanDesc, |
| contentType, AtomicReference.class); |
| |
| // Need to support global legacy setting, for now: |
| JsonInclude.Include incl = (inclV == null) ? JsonInclude.Include.USE_DEFAULTS : inclV.getContentInclusion(); |
| Object valueToSuppress; |
| boolean suppressNulls; |
| |
| if (incl == JsonInclude.Include.USE_DEFAULTS |
| || incl == JsonInclude.Include.ALWAYS) { |
| valueToSuppress = null; |
| suppressNulls = false; |
| } else { |
| suppressNulls = true; |
| switch (incl) { |
| case NON_DEFAULT: |
| valueToSuppress = BeanUtil.getDefaultValue(contentType); |
| if (valueToSuppress != null) { |
| if (valueToSuppress.getClass().isArray()) { |
| valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress); |
| } |
| } |
| break; |
| case NON_ABSENT: |
| valueToSuppress = contentType.isReferenceType() |
| ? MapSerializer.MARKER_FOR_EMPTY : null; |
| break; |
| case NON_EMPTY: |
| valueToSuppress = MapSerializer.MARKER_FOR_EMPTY; |
| break; |
| case CUSTOM: |
| valueToSuppress = prov.includeFilterInstance(null, inclV.getContentFilter()); |
| if (valueToSuppress == null) { // is this legal? |
| suppressNulls = true; |
| } else { |
| suppressNulls = prov.includeFilterSuppressNulls(valueToSuppress); |
| } |
| break; |
| case NON_NULL: |
| default: // should not matter but... |
| valueToSuppress = null; |
| break; |
| } |
| } |
| AtomicReferenceSerializer ser = new AtomicReferenceSerializer(refType, staticTyping, |
| contentTypeSerializer, contentSerializer); |
| return ser.withContentInclusion(valueToSuppress, suppressNulls); |
| } |
| |
| /* |
| /********************************************************** |
| /* Factory methods, for non-container types |
| /********************************************************** |
| */ |
| |
| /** |
| * @since 2.5 |
| */ |
| protected JsonSerializer<?> buildIteratorSerializer(SerializationConfig config, |
| JavaType type, BeanDescription beanDesc, boolean staticTyping, |
| JavaType valueType) |
| throws JsonMappingException |
| { |
| return new IteratorSerializer(valueType, staticTyping, createTypeSerializer(config, valueType)); |
| } |
| |
| /** |
| * @since 2.5 |
| */ |
| protected JsonSerializer<?> buildIterableSerializer(SerializationConfig config, |
| JavaType type, BeanDescription beanDesc, boolean staticTyping, |
| JavaType valueType) |
| throws JsonMappingException |
| { |
| return new IterableSerializer(valueType, staticTyping, createTypeSerializer(config, valueType)); |
| } |
| |
| protected JsonSerializer<?> buildEnumSerializer(SerializationConfig config, |
| JavaType type, BeanDescription beanDesc) |
| throws JsonMappingException |
| { |
| /* As per [databind#24], may want to use alternate shape, serialize as JSON Object. |
| * Challenge here is that EnumSerializer does not know how to produce |
| * POJO style serialization, so we must handle that special case separately; |
| * otherwise pass it to EnumSerializer. |
| */ |
| JsonFormat.Value format = beanDesc.findExpectedFormat(null); |
| if (format != null && format.getShape() == JsonFormat.Shape.OBJECT) { |
| // one special case: suppress serialization of "getDeclaringClass()"... |
| ((BasicBeanDescription) beanDesc).removeProperty("declaringClass"); |
| // returning null will mean that eventually BeanSerializer gets constructed |
| return null; |
| } |
| @SuppressWarnings("unchecked") |
| Class<Enum<?>> enumClass = (Class<Enum<?>>) type.getRawClass(); |
| JsonSerializer<?> ser = EnumSerializer.construct(enumClass, config, beanDesc, format); |
| // [databind#120]: Allow post-processing |
| if (_factoryConfig.hasSerializerModifiers()) { |
| for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) { |
| ser = mod.modifyEnumSerializer(config, type, beanDesc, ser); |
| } |
| } |
| return ser; |
| } |
| |
| /* |
| /********************************************************** |
| /* Other helper methods |
| /********************************************************** |
| */ |
| |
| /** |
| * Helper method called to try to find whether there is an annotation in the |
| * class that indicates key serializer to use. |
| * If so, will try to instantiate key serializer and return it; otherwise returns null. |
| */ |
| protected JsonSerializer<Object> _findKeySerializer(SerializerProvider prov, |
| Annotated a) |
| throws JsonMappingException |
| { |
| AnnotationIntrospector intr = prov.getAnnotationIntrospector(); |
| Object serDef = intr.findKeySerializer(a); |
| if (serDef != null) { |
| return prov.serializerInstance(a, serDef); |
| } |
| return null; |
| } |
| |
| /** |
| * Helper method called to try to find whether there is an annotation in the |
| * class that indicates content ("value") serializer to use. |
| * If so, will try to instantiate value serializer and return it; otherwise returns null. |
| */ |
| protected JsonSerializer<Object> _findContentSerializer(SerializerProvider prov, |
| Annotated a) |
| throws JsonMappingException |
| { |
| AnnotationIntrospector intr = prov.getAnnotationIntrospector(); |
| Object serDef = intr.findContentSerializer(a); |
| if (serDef != null) { |
| return prov.serializerInstance(a, serDef); |
| } |
| return null; |
| } |
| |
| /** |
| * Method called to find filter that is configured to be used with bean |
| * serializer being built, if any. |
| */ |
| protected Object findFilterId(SerializationConfig config, BeanDescription beanDesc) { |
| return config.getAnnotationIntrospector().findFilterId((Annotated)beanDesc.getClassInfo()); |
| } |
| |
| /** |
| * Helper method to check whether global settings and/or class |
| * annotations for the bean class indicate that static typing |
| * (declared types) should be used for properties. |
| * (instead of dynamic runtime types). |
| * |
| * @since 2.1 (earlier had variant with additional 'property' parameter) |
| */ |
| protected boolean usesStaticTyping(SerializationConfig config, |
| BeanDescription beanDesc, TypeSerializer typeSer) |
| { |
| /* 16-Aug-2010, tatu: If there is a (value) type serializer, we cannot force |
| * static typing; that would make it impossible to handle expected subtypes |
| */ |
| if (typeSer != null) { |
| return false; |
| } |
| AnnotationIntrospector intr = config.getAnnotationIntrospector(); |
| JsonSerialize.Typing t = intr.findSerializationTyping(beanDesc.getClassInfo()); |
| if (t != null && t != JsonSerialize.Typing.DEFAULT_TYPING) { |
| return (t == JsonSerialize.Typing.STATIC); |
| } |
| return config.isEnabled(MapperFeature.USE_STATIC_TYPING); |
| } |
| |
| // Commented out in 2.9 |
| /* |
| protected Class<?> _verifyAsClass(Object src, String methodName, Class<?> noneClass) |
| { |
| if (src == null) { |
| return null; |
| } |
| if (!(src instanceof Class)) { |
| throw new IllegalStateException("AnnotationIntrospector."+methodName+"() returned value of type "+src.getClass().getName()+": expected type JsonSerializer or Class<JsonSerializer> instead"); |
| } |
| Class<?> cls = (Class<?>) src; |
| if (cls == noneClass || ClassUtil.isBogusClass(cls)) { |
| return null; |
| } |
| return cls; |
| } |
| */ |
| } |