Skip to content

Third part of #4907: change ordering of serializer lookups to defer introspection further #5172

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package tools.jackson.databind.ser;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
Expand Down Expand Up @@ -50,51 +48,6 @@ 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, ValueSerializer<?>> _concrete;

static {
HashMap<String, ValueSerializer<?>> concrete
= new HashMap<String, ValueSerializer<?>>();


/* String and string-like types (note: date types explicitly
* not included -- can use either textual or numeric serialization)
*/
concrete.put(String.class.getName(), StringSerializer.instance);
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(), JavaUtilCalendarSerializer.instance);
concrete.put(java.util.Date.class.getName(), JavaUtilDateSerializer.instance);

_concrete = concrete;
}

/*
/**********************************************************************
/* Configuration
Expand Down Expand Up @@ -277,27 +230,10 @@ public TypeSerializer findPropertyContentTypeSerializer(SerializationContext ctx

/*
/**********************************************************************
/* Overridable secondary serializer accessor methods
/* 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 ValueSerializer<?> findSerializerByLookup(JavaType type,
SerializationConfig config, BeanDescription.Supplier beanDescRef,
JsonFormat.Value format, boolean staticTyping)
{
final Class<?> raw = type.getRawClass();
ValueSerializer<?> ser = JDKMiscSerializers.find(raw);
if (ser == null) {
final String clsName = raw.getName();
ser = _concrete.get(clsName);
}
return ser;
}

/**
* Method called to see if one of primary per-class annotations
* (or related, like implementing of {@link JacksonSerializable})
Expand Down Expand Up @@ -347,15 +283,28 @@ protected final ValueSerializer<?> findSerializerByPrimaryType(SerializationCont
JavaType type, BeanDescription.Supplier beanDescRef, JsonFormat.Value formatOverrides,
boolean staticTyping)
{
// First: simple lookups for concrete types
final Class<?> raw = type.getRawClass();
ValueSerializer<?> ser;

if ((ser = JDKCoreSerializers.find(raw)) != null) {
return ser;
}
if ((ser = JDKStringLikeSerializer.find(raw)) != null) {
return ser;
}
if ((ser = JDKMiscSerializers.find(raw)) != null) {
return ser;
}

if (type.isTypeOrSubTypeOf(Calendar.class)) {
return JavaUtilCalendarSerializer.instance;
}
if (type.isTypeOrSubTypeOf(Date.class)) {
// 06-Nov-2020, tatu: Strange precedence challenge; need to consider
// "java.sql.Date" unfortunately
if (!type.hasRawClass(Date.class)) {
ValueSerializer<?> ser = OptionalHandlerFactory.instance.findSerializer(ctxt.getConfig(), type);
if (ser != null) {
if ((ser = OptionalHandlerFactory.instance.findSerializer(ctxt.getConfig(), type) ) != null) {
return ser;
}
}
Expand All @@ -382,12 +331,7 @@ protected final ValueSerializer<?> findSerializerByPrimaryType(SerializationCont
}
return NumberSerializer.instance;
}
if (type.isEnumType()) {
return buildEnumSerializer(ctxt, type, beanDescRef,
_calculateEffectiveFormat(beanDescRef, Enum.class, formatOverrides));
}
Class<?> raw = type.getRawClass();
if (Map.Entry.class.isAssignableFrom(raw)) {
if (type.isTypeOrSubTypeOf(Map.Entry.class)) {
// 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
Expand Down
38 changes: 19 additions & 19 deletions src/main/java/tools/jackson/databind/ser/BeanSerializerFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -207,48 +207,48 @@ protected ValueSerializer<?> _createSerializer2(SerializationContext ctxt,
formatOverrides, staticTyping);
} else if (type.isEnumType()) {
for (Serializers serializers : customSerializers()) {
ser = serializers.findEnumSerializer(config, type, beanDescRef, formatOverrides);
if (ser != null) {
if ((ser = serializers.findEnumSerializer(config, type, beanDescRef, formatOverrides)) != null) {
break;
}
}
} else if (type.isTypeOrSubTypeOf(TreeNode.class)) {
for (Serializers serializers : customSerializers()) {
ser = serializers.findTreeNodeSerializer(config, type, beanDescRef, formatOverrides);
if (ser != null) {
if ((ser = serializers.findTreeNodeSerializer(config, type, beanDescRef, formatOverrides)) != null) {
break;
}
}
} else {
// Modules may provide serializers of POJO types:
for (Serializers serializers : customSerializers()) {
ser = serializers.findSerializer(config, type, beanDescRef, formatOverrides);
if (ser != null) {
if ((ser = serializers.findSerializer(config, type, beanDescRef, formatOverrides)) != null) {
break;
}
}
}
// 25-Jun-2015, tatu: Then JacksonSerializable, @JsonValue etc. NOTE! Prior to 2.6,
// this call was BEFORE custom serializer lookup, which was wrong.
if (ser == null) {
ser = findSerializerByAnnotations(ctxt, type, beanDescRef);
}
}

if (ser == null) {
// Otherwise, we will check "primary types"; main types that have
// precedence over POJO handling
ser = findSerializerByLookup(type, config, beanDescRef, formatOverrides, staticTyping);
ser = findSerializerByPrimaryType(ctxt, type, beanDescRef, formatOverrides, staticTyping);
if (ser == null) {
ser = findSerializerByPrimaryType(ctxt, type, beanDescRef, formatOverrides, staticTyping);
// Then JacksonSerializable, @JsonValue etc.
ser = findSerializerByAnnotations(ctxt, type, beanDescRef);
if (ser == null) {
// And this is where this class comes in: if type is not a
// known "primary JDK type", perhaps it's a bean? We can still
// get a null, if we can't find a single suitable bean property.
ser = constructBeanOrAddOnSerializer(ctxt, type, beanDescRef, formatOverrides, staticTyping);
// Finally: maybe we can still deal with it as an implementation of some basic JDK interface?
// ... but annotations lookup must predate Enum lookup
if (type.isEnumType()) {
// NOTE: may still return `null` (with Shape override)
ser = buildEnumSerializer(ctxt, type, beanDescRef,
_calculateEffectiveFormat(beanDescRef, Enum.class, formatOverrides));
}
if (ser == null) {
ser = ctxt.getUnknownTypeSerializer(beanDescRef.getBeanClass());
// And this is where this class comes in: if type is not a
// known "primary JDK type", perhaps it's a POJO (aka Bean)?
// We can still get a null, for various reasons
ser = constructBeanOrAddOnSerializer(ctxt, type, beanDescRef, formatOverrides, staticTyping);
if (ser == null) {
ser = ctxt.getUnknownTypeSerializer(beanDescRef.getBeanClass());
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package tools.jackson.databind.ser.jdk;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashMap;

import tools.jackson.databind.ValueSerializer;
import tools.jackson.databind.ser.BasicSerializerFactory;
import tools.jackson.databind.ser.std.ToStringSerializer;

/**
* Set of serializers for JDK core types.
*
* @since 3.0
*/
public class JDKCoreSerializers
{
/**
* 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, ValueSerializer<?>> _concrete;

static {
HashMap<String, ValueSerializer<?>> concrete = new HashMap<>();

// String and string-like types (note: date types explicitly
// not included -- can use either textual or numeric serialization)
concrete.put(String.class.getName(), StringSerializer.instance);
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));

_concrete = concrete;
}

/**
* Method called by {@link BasicSerializerFactory} to find one of serializers provided here.
*/
public static final ValueSerializer<?> find(Class<?> raw)
{
return _concrete.get(raw.getName());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ public class JDKMiscSerializers
*/
public static final ValueSerializer<?> find(Class<?> raw)
{
ValueSerializer<?> ser = JDKStringLikeSerializer.find(raw);
if (ser != null) {
return ser;
}
if (raw == UUID.class) {
return new UUIDSerializer();
}
Expand Down