ValueSerializer.java
package tools.jackson.databind;
import java.util.Iterator;
import java.util.Set;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import tools.jackson.core.*;
import tools.jackson.databind.jsonFormatVisitors.JsonFormatVisitable;
import tools.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import tools.jackson.databind.jsontype.TypeSerializer;
import tools.jackson.databind.ser.PropertyWriter;
import tools.jackson.databind.util.ClassUtil;
import tools.jackson.databind.util.NameTransformer;
/**
* Abstract class that defines API used by {@link ObjectMapper} (and
* other chained {@link ValueSerializer}s too) to serialize Objects of
* arbitrary types into JSON, using provided {@link JsonGenerator}.
* Note that although API is defined here, custom serializer implementations
* should almost always be based on {@link tools.jackson.databind.ser.std.StdSerializer}
* since it will implement many of optional methods of this class.
*<p>
* If serializer is an aggregate one -- meaning it delegates handling of some
* of its contents by using other serializer(s) -- it typically also needs
* to implement {@link #resolve} which can locate secondary serializers needed.
* This is important to allow dynamic
* overrides of serializers; separate call interface is needed to separate
* resolution of secondary serializers (which may have cyclic link back
* to serializer itself, directly or indirectly).
*<p>
* Initialization of serializers is handled by two main methods:
*<ol>
* <li>{@link #resolve}: called after instance is configured to be used for specific type,
* but without yet knowing property it will be used for (or, in case of root values, without property).
* Method needs to be implemented for serializers that may work on cyclic types, and specifically
* is implemented by standard POJO serializer ({@code BeanSerializer}). It is usually not needed for
* container types as their type definitions are not cyclic, unlike some POJO types.
* <li>{@link #createContextual}: called on resolved instance (whether newly created, or found via cache),
* when serializer is to be used for specific property, or as root value serializer (no referring property).
* It is used to apply annotations from property accessors (getter, field), and may also be used for resolving
* nested types for container serializers (such as ones for {@link java.util.Collection}s).
* </ol>
* Caching of serializers occurs after {@link #resolve} is called: cached instances are not contextual.
*<p>
* NOTE: various <code>serialize</code> methods are never (to be) called
* with null values -- caller <b>must</b> handle null values, usually
* by calling {@link SerializationContext#findNullValueSerializer} to obtain
* serializer to use.
* This also means that custom serializers cannot be directly used to change
* the output to produce when serializing null values.
*<p>
* NOTE: In Jackson 2.x was named {@code JsonSerializer}
*/
public abstract class ValueSerializer<T>
implements JsonFormatVisitable
{
/*
/**********************************************************************
/* Initialization, with former `ResolvableSerializer`,
/* `ContextualSerializer`.
/**********************************************************************
*/
/**
* Method called after {@link SerializationContext} has registered
* the serializer, but before it has returned it to the caller.
* Called object can then resolve its dependencies to other types,
* including self-references (direct or indirect).
*<p>
* Note that this method does NOT return serializer, since resolution
* is not allowed to change actual serializer to use.
*
* @param ctxt Currently active serialization context.
*/
public void resolve(SerializationContext ctxt) {
// Default implementation does nothing
}
/**
* Method called to see if a different (or differently configured) serializer
* is needed to serialize values of specified property (or, for root values, in which
* case `null` is passed).
* Note that instance that this method is called on is typically shared one and
* as a result method should <b>NOT</b> modify this instance but rather construct
* and return a new instance. This instance should only be returned as-is, in case
* it is already suitable for use.
*<p>
* Note that method is only called once per POJO property, and for the first usage as root
* value serializer; it is not called for every serialization, as doing that would have
* significant performance impact; most serializers cache contextual instances for future
* use.
*
* @param ctxt Context to use for accessing config, other serializers
* @param property Property (defined by one or more accessors - field or method - used
* for accessing logical property value) for which serializer is used to be used;
* or, `null` for root value (or in cases where caller does not have this information,
* which is handled as root value case).
*
* @return Serializer to use for serializing values of specified property;
* may be this instance or a new instance.
*/
public ValueSerializer<?> createContextual(SerializationContext ctxt,
BeanProperty property) {
// default implementation returns instance unmodified
return this;
}
/*
/**********************************************************************
/* Fluent factory methods for constructing decorated versions
/**********************************************************************
*/
/**
* Method that will return serializer instance that produces
* "unwrapped" serialization, if applicable for type being
* serialized (which is the case for some serializers
* that produce JSON Objects as output).
* If no unwrapped serializer can be constructed, will simply
* return serializer as-is.
*<p>
* Default implementation just returns serializer as-is,
* indicating that no unwrapped variant exists
*
* @param unwrapper Name transformation to use to convert between names
* of unwrapper properties
*/
public ValueSerializer<T> unwrappingSerializer(NameTransformer unwrapper) {
return this;
}
/**
* Method that can be called to try to replace serializer this serializer
* delegates calls to. If not supported (either this serializer does not
* delegate anything; or it does not want any changes), should either
* throw {@link UnsupportedOperationException} (if operation does not
* make sense or is not allowed); or return this serializer as is.
*/
public ValueSerializer<T> replaceDelegatee(ValueSerializer<?> delegatee) {
throw new UnsupportedOperationException();
}
/**
* Mutant factory method that is called if contextual configuration indicates that
* a specific filter (as specified by <code>filterId</code>) is to be used for
* serialization.
*<p>
* Default implementation simply returns <code>this</code>; sub-classes that do support
* filtering will need to create and return new instance if filter changes.
*/
public ValueSerializer<?> withFilterId(Object filterId) {
return this;
}
/**
* Mutant factory method called to create a new instance after excluding specified set of
* properties by name, if there is any.
*
* @param ignoredProperties Set of property names to ignore for serialization;
* @return Serializer instance that without specified set of properties to ignore (if any)
*/
public ValueSerializer<?> withIgnoredProperties(Set<String> ignoredProperties) {
return this;
}
/**
* Mutant factory called if there is need to create a serializer with specified
* format overrides (typically from property on which this serializer would be used,
* based on type declaration). Method is called before {@link #createContextual}
* but right after serializer is either constructed or fetched from cache.
*<p>
* Method can do one of three things:
*<ul>
* <li>Return {@code this} instance as is: this means that none of overrides has any effect
* </li>
* <li>Return an alternate {@link ValueSerializer}, suitable for use with specified format
* </li>
* <li>Return {@code null} to indicate that this serializer instance is not suitable for
* handling format variation, but does not know how to construct new serializer: caller
* will typically then call {@link tools.jackson.databind.ser.SerializerFactory} with overrides to construct new serializer
* </li>
*</ul>
* One example of second approach is the case where {@link com.fasterxml.jackson.annotation.JsonFormat.Shape#STRING} indicates String
* representation and code can just construct simple "string-like serializer", or variant of itself
* (similar to how {@link #createContextual} is often implemented).
* And third case (returning {@code null}) is applicable for cases like format defines
* {@link com.fasterxml.jackson.annotation.JsonFormat.Shape#POJO}, requesting "introspect serializer for POJO regardless of type":
* {@link tools.jackson.databind.ser.SerializerFactory} is needed for full re-introspection, typically.
*
* @param formatOverrides (not null) Override settings, NOT including original format settings (which
* serializer needs to explicitly retain if needed)
*
* @since 3.0
*/
public ValueSerializer<?> withFormatOverrides(SerializationConfig config,
JsonFormat.Value formatOverrides)
{
// First: if no override, safe to use as is:
if (formatOverrides.getShape() == JsonFormat.Shape.ANY) {
return this;
}
return null;
}
/*
/**********************************************************************
/* Serialization methods
/**********************************************************************
*/
/**
* Method that can be called to ask implementation to serialize
* values of type this serializer handles.
*
* @param value Value to serialize; can <b>not</b> be null.
* @param gen Generator used to output resulting Json content
* @param ctxt Context that can be used to get serializers for
* serializing Objects value contains, if any.
*/
public abstract void serialize(T value, JsonGenerator gen, SerializationContext ctxt)
throws JacksonException;
/**
* Method that can be called to ask implementation to serialize
* values of type this serializer handles, using specified type serializer
* for embedding necessary type information.
*<p>
* Default implementation will throw {@link UnsupportedOperationException}
* to indicate that proper type handling needs to be implemented.
*<p>
* For simple datatypes written as a single scalar value (JSON String, Number, Boolean),
* implementation would look like:
*<pre>
* // note: method to call depends on whether this type is serialized as JSON scalar, object or Array!
* typeSer.writeTypePrefixForScalar(value, gen);
* serialize(value, gen, provider);
* typeSer.writeTypeSuffixForScalar(value, gen);
*</pre>
* and implementations for type serialized as JSON Arrays or Objects would differ slightly,
* as <code>START-ARRAY</code>/<code>END-ARRAY</code> and
* <code>START-OBJECT</code>/<code>END-OBJECT</code> pairs
* need to be properly handled with respect to serializing of contents.
*
* @param value Value to serialize; can <b>not</b> be null.
* @param gen Generator used to output resulting Json content
* @param ctxt Context that can be used to get serializers for
* serializing Objects value contains, if any.
* @param typeSer Type serializer to use for including type information
*/
public void serializeWithType(T value, JsonGenerator gen, SerializationContext ctxt,
TypeSerializer typeSer)
throws JacksonException
{
// 07-Dec-2025, tatu: [databind#1654] Check for "no-op" type serializer
// indirectly
if (typeSer.getTypeInclusion() == As.NOTHING) {
serialize(value, gen, ctxt);
return;
}
Class<?> clz = handledType();
if (clz == null) {
clz = value.getClass();
}
ctxt.reportBadDefinition(clz, String.format(
"Type id handling (method `serializeWithType()`) not implemented for type %s (by serializer of type %s)",
ClassUtil.nameOf(clz), ClassUtil.nameOf(getClass())));
}
/*
/**********************************************************************
/* Accessors for serializer metadata
/**********************************************************************
*/
/**
* Method for accessing type of Objects this serializer can handle.
* Note that this information is not guaranteed to be exact -- it
* may be a more generic (super-type) -- but it should not be
* incorrect (return a non-related type).
*<p>
* NOTE: starting with 3.0, left {@code abstract}.
*/
public Class<?> handledType() { return Object.class; }
/**
* Method that can be called to see whether this serializer instance
* will use Object Id to handle cyclic references.
*/
public boolean usesObjectId() {
return false;
}
/**
* Accessor for checking whether this serializer is an
* "unwrapping" serializer; this is necessary to know since
* it may also require caller to suppress writing of the
* leading property name.
*/
public boolean isUnwrappingSerializer() {
return false;
}
/**
* Accessor that can be used to determine if this serializer uses
* another serializer for actual serialization, by delegating
* calls. If so, will return immediate delegate (which itself may
* delegate to further serializers); otherwise will return null.
*
* @return Serializer this serializer delegates calls to, if null;
* null otherwise.
*/
public ValueSerializer<?> getDelegatee() {
return null;
}
/**
* Accessor for iterating over logical properties that the type
* handled by this serializer has, from serialization perspective.
* Actual type of properties, if any, will be
* {@link tools.jackson.databind.ser.BeanPropertyWriter}.
* Of standard Jackson serializers, only {@link tools.jackson.databind.ser.BeanSerializer}
* exposes properties.
*/
public Iterator<PropertyWriter> properties() {
return ClassUtil.emptyIterator();
}
/*
/**********************************************************************
/* Accessors for introspecting handling of values
/**********************************************************************
*/
/**
* Method called to check whether given serializable value is
* considered "empty" value (for purposes of suppressing serialization
* of empty values).
*<p>
* Default implementation will consider only null values to be empty.
*/
public boolean isEmpty(SerializationContext ctxt, T value) {
return (value == null);
}
/*
/**********************************************************************
/* Default JsonFormatVisitable implementation
/**********************************************************************
*/
/**
* Default implementation simply calls {@link JsonFormatVisitorWrapper#expectAnyFormat(JavaType)}.
*/
@Override
public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType type)
{
visitor.expectAnyFormat(type);
}
/*
/**********************************************************************
/* Helper class(es)
/**********************************************************************
*/
/**
* This marker class is only to be used with annotations, to
* indicate that <b>no serializer is configured</b>.
*<p>
* Specifically, this class is to be used as the marker for
* annotation {@link tools.jackson.databind.annotation.JsonSerialize}.
*/
public abstract static class None
extends ValueSerializer<Object> { }
}