Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,10 @@ public DeserializationConfig withRootName(PropertyName rootName) {
return new DeserializationConfig(this, rootName);
}

public DeserializationConfig withRadix(int radix) {
return _withBase(_base.withRadix(radix));
}

@Override
public DeserializationConfig withView(Class<?> view) {
return (_view == view) ? this : new DeserializationConfig(this, view);
Expand Down
14 changes: 13 additions & 1 deletion src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ public boolean useForType(JavaType t)
// 16-May-2009, tatu: Ditto ^^^
protected final static AnnotationIntrospector DEFAULT_ANNOTATION_INTROSPECTOR = new JacksonAnnotationIntrospector();

protected static final int DEFAULT_RADIX = 10;
/**
* Base settings contain defaults used for all {@link ObjectMapper}
* instances.
Expand All @@ -419,7 +420,7 @@ public boolean useForType(JavaType t)
// Since 2.12:
new DefaultAccessorNamingStrategy.Provider(),
// Since 2.16: [databind#2502] Add a way to configure Caches Jackson uses
DefaultCacheProvider.defaultInstance()
DefaultCacheProvider.defaultInstance(), DEFAULT_RADIX
);

/*
Expand Down Expand Up @@ -2545,6 +2546,17 @@ public ObjectMapper setDateFormat(DateFormat dateFormat)
return this;
}

/**
* Method for configuring the radix to use when serializing integral types
* as strings.
*/
public ObjectMapper setRadix(int radix)
{
_deserializationConfig = _deserializationConfig.withRadix(radix);
_serializationConfig = _serializationConfig.withRadix(radix);
return this;
}

/**
* @since 2.5
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,10 @@ public SerializationConfig with(SubtypeResolver str) {
return (str == _subtypeResolver)? this : new SerializationConfig(this, str);
}

public SerializationConfig withRadix(int radix) {
return _withBase(_base.withRadix(radix));
}

@Override
public SerializationConfig withView(Class<?> view) {
return (_view == view) ? this : new SerializationConfig(this, view);
Expand Down
89 changes: 58 additions & 31 deletions src/main/java/com/fasterxml/jackson/databind/cfg/BaseSettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,11 @@ public final class BaseSettings
*/
protected final CacheProvider _cacheProvider;

/**
* Default radix to use when serializing/deserializing integers to string.
*/
protected final int _defaultRadix;

/*
/**********************************************************
/* Construction
Expand All @@ -165,11 +170,11 @@ public final class BaseSettings
* @since 2.19
*/
public BaseSettings(ClassIntrospector ci, AnnotationIntrospector ai,
PropertyNamingStrategy pns, EnumNamingStrategy ens, TypeFactory tf,
TypeResolverBuilder<?> typer, DateFormat dateFormat, HandlerInstantiator hi,
Locale locale, TimeZone tz, Base64Variant defaultBase64,
PolymorphicTypeValidator ptv, AccessorNamingStrategy.Provider accNaming,
CacheProvider cacheProvider)
PropertyNamingStrategy pns, EnumNamingStrategy ens, TypeFactory tf,
TypeResolverBuilder<?> typer, DateFormat dateFormat, HandlerInstantiator hi,
Locale locale, TimeZone tz, Base64Variant defaultBase64,
PolymorphicTypeValidator ptv, AccessorNamingStrategy.Provider accNaming,
CacheProvider cacheProvider, int defaultRadix)
{
_classIntrospector = ci;
_annotationIntrospector = ai;
Expand All @@ -185,6 +190,7 @@ public BaseSettings(ClassIntrospector ci, AnnotationIntrospector ai,
_typeValidator = ptv;
_accessorNaming = accNaming;
_cacheProvider = cacheProvider;
_defaultRadix = defaultRadix;
}

/**
Expand All @@ -193,13 +199,13 @@ public BaseSettings(ClassIntrospector ci, AnnotationIntrospector ai,
*/
@Deprecated
public BaseSettings(ClassIntrospector ci, AnnotationIntrospector ai,
PropertyNamingStrategy pns, TypeFactory tf,
TypeResolverBuilder<?> typer, DateFormat dateFormat, HandlerInstantiator hi,
Locale locale, TimeZone tz, Base64Variant defaultBase64,
PolymorphicTypeValidator ptv, AccessorNamingStrategy.Provider accNaming,
CacheProvider cacheProvider)
PropertyNamingStrategy pns, TypeFactory tf,
TypeResolverBuilder<?> typer, DateFormat dateFormat, HandlerInstantiator hi,
Locale locale, TimeZone tz, Base64Variant defaultBase64,
PolymorphicTypeValidator ptv, AccessorNamingStrategy.Provider accNaming,
CacheProvider cacheProvider, int defaultRadix)
{
this(ci, ai, pns, null, tf, typer, dateFormat, hi, locale, tz, defaultBase64, ptv, accNaming, cacheProvider);
this(ci, ai, pns, null, tf, typer, dateFormat, hi, locale, tz, defaultBase64, ptv, accNaming, cacheProvider, defaultRadix);
}

/**
Expand All @@ -208,13 +214,13 @@ public BaseSettings(ClassIntrospector ci, AnnotationIntrospector ai,
*/
@Deprecated
public BaseSettings(ClassIntrospector ci, AnnotationIntrospector ai,
PropertyNamingStrategy pns, TypeFactory tf,
TypeResolverBuilder<?> typer, DateFormat dateFormat, HandlerInstantiator hi,
Locale locale, TimeZone tz, Base64Variant defaultBase64,
PolymorphicTypeValidator ptv, AccessorNamingStrategy.Provider accNaming)
PropertyNamingStrategy pns, TypeFactory tf,
TypeResolverBuilder<?> typer, DateFormat dateFormat, HandlerInstantiator hi,
Locale locale, TimeZone tz, Base64Variant defaultBase64,
PolymorphicTypeValidator ptv, AccessorNamingStrategy.Provider accNaming, int defaultRadix)
{
this(ci, ai, pns, tf, typer, dateFormat, hi, locale, tz, defaultBase64, ptv, accNaming,
DefaultCacheProvider.defaultInstance());
DefaultCacheProvider.defaultInstance(), defaultRadix);
}

/**
Expand All @@ -237,7 +243,8 @@ public BaseSettings copy() {
_defaultBase64,
_typeValidator,
_accessorNaming,
_cacheProvider);
_cacheProvider,
_defaultRadix);
}

/*
Expand All @@ -252,7 +259,7 @@ public BaseSettings withClassIntrospector(ClassIntrospector ci) {
}
return new BaseSettings(ci, _annotationIntrospector, _propertyNamingStrategy, _enumNamingStrategy,
_typeFactory, _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
_timeZone, _defaultBase64, _typeValidator, _accessorNaming, _cacheProvider);
_timeZone, _defaultBase64, _typeValidator, _accessorNaming, _cacheProvider, _defaultRadix);
}

public BaseSettings withAnnotationIntrospector(AnnotationIntrospector ai) {
Expand All @@ -261,7 +268,7 @@ public BaseSettings withAnnotationIntrospector(AnnotationIntrospector ai) {
}
return new BaseSettings(_classIntrospector, ai, _propertyNamingStrategy, _enumNamingStrategy,
_typeFactory, _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
_timeZone, _defaultBase64, _typeValidator, _accessorNaming, _cacheProvider);
_timeZone, _defaultBase64, _typeValidator, _accessorNaming, _cacheProvider, _defaultRadix);
}

public BaseSettings withInsertedAnnotationIntrospector(AnnotationIntrospector ai) {
Expand All @@ -288,7 +295,7 @@ public BaseSettings withPropertyNamingStrategy(PropertyNamingStrategy pns) {
}
return new BaseSettings(_classIntrospector, _annotationIntrospector, pns, _enumNamingStrategy,
_typeFactory, _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
_timeZone, _defaultBase64, _typeValidator, _accessorNaming, _cacheProvider);
_timeZone, _defaultBase64, _typeValidator, _accessorNaming, _cacheProvider, _defaultRadix);
}

/**
Expand All @@ -300,7 +307,7 @@ public BaseSettings withEnumNamingStrategy(EnumNamingStrategy ens) {
}
return new BaseSettings(_classIntrospector, _annotationIntrospector, _propertyNamingStrategy, ens,
_typeFactory, _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
_timeZone, _defaultBase64, _typeValidator, _accessorNaming, _cacheProvider);
_timeZone, _defaultBase64, _typeValidator, _accessorNaming, _cacheProvider, _defaultRadix);
}

// @since 2.12
Expand All @@ -310,7 +317,7 @@ public BaseSettings withAccessorNaming(AccessorNamingStrategy.Provider p) {
}
return new BaseSettings(_classIntrospector, _annotationIntrospector, _propertyNamingStrategy, _enumNamingStrategy,
_typeFactory, _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
_timeZone, _defaultBase64, _typeValidator, p, _cacheProvider);
_timeZone, _defaultBase64, _typeValidator, p, _cacheProvider, _defaultRadix);
}

public BaseSettings withTypeFactory(TypeFactory tf) {
Expand All @@ -319,7 +326,7 @@ public BaseSettings withTypeFactory(TypeFactory tf) {
}
return new BaseSettings(_classIntrospector, _annotationIntrospector, _propertyNamingStrategy, _enumNamingStrategy,
tf, _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
_timeZone, _defaultBase64, _typeValidator, _accessorNaming, _cacheProvider);
_timeZone, _defaultBase64, _typeValidator, _accessorNaming, _cacheProvider, _defaultRadix);
}

public BaseSettings withTypeResolverBuilder(TypeResolverBuilder<?> typer) {
Expand All @@ -328,7 +335,7 @@ public BaseSettings withTypeResolverBuilder(TypeResolverBuilder<?> typer) {
}
return new BaseSettings(_classIntrospector, _annotationIntrospector, _propertyNamingStrategy, _enumNamingStrategy,
_typeFactory, typer, _dateFormat, _handlerInstantiator, _locale,
_timeZone, _defaultBase64, _typeValidator, _accessorNaming, _cacheProvider);
_timeZone, _defaultBase64, _typeValidator, _accessorNaming, _cacheProvider, _defaultRadix);
}

public BaseSettings withDateFormat(DateFormat df) {
Expand All @@ -342,7 +349,7 @@ public BaseSettings withDateFormat(DateFormat df) {
}
return new BaseSettings(_classIntrospector, _annotationIntrospector, _propertyNamingStrategy, _enumNamingStrategy,
_typeFactory, _typeResolverBuilder, df, _handlerInstantiator, _locale,
_timeZone, _defaultBase64, _typeValidator, _accessorNaming, _cacheProvider);
_timeZone, _defaultBase64, _typeValidator, _accessorNaming, _cacheProvider, _defaultRadix);
}

public BaseSettings withHandlerInstantiator(HandlerInstantiator hi) {
Expand All @@ -351,7 +358,7 @@ public BaseSettings withHandlerInstantiator(HandlerInstantiator hi) {
}
return new BaseSettings(_classIntrospector, _annotationIntrospector, _propertyNamingStrategy, _enumNamingStrategy,
_typeFactory, _typeResolverBuilder, _dateFormat, hi, _locale,
_timeZone, _defaultBase64, _typeValidator, _accessorNaming, _cacheProvider);
_timeZone, _defaultBase64, _typeValidator, _accessorNaming, _cacheProvider, _defaultRadix);
}

public BaseSettings with(Locale l) {
Expand All @@ -360,7 +367,7 @@ public BaseSettings with(Locale l) {
}
return new BaseSettings(_classIntrospector, _annotationIntrospector, _propertyNamingStrategy, _enumNamingStrategy,
_typeFactory,_typeResolverBuilder, _dateFormat, _handlerInstantiator, l,
_timeZone, _defaultBase64, _typeValidator, _accessorNaming, _cacheProvider);
_timeZone, _defaultBase64, _typeValidator, _accessorNaming, _cacheProvider, _defaultRadix);
}

/**
Expand All @@ -383,7 +390,7 @@ public BaseSettings with(TimeZone tz)
return new BaseSettings(_classIntrospector, _annotationIntrospector,
_propertyNamingStrategy, _enumNamingStrategy, _typeFactory,
_typeResolverBuilder, df, _handlerInstantiator, _locale,
tz, _defaultBase64, _typeValidator, _accessorNaming, _cacheProvider);
tz, _defaultBase64, _typeValidator, _accessorNaming, _cacheProvider, _defaultRadix);
}

/**
Expand All @@ -396,7 +403,7 @@ public BaseSettings with(Base64Variant base64) {
return new BaseSettings(_classIntrospector, _annotationIntrospector,
_propertyNamingStrategy, _enumNamingStrategy, _typeFactory,
_typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
_timeZone, base64, _typeValidator, _accessorNaming, _cacheProvider);
_timeZone, base64, _typeValidator, _accessorNaming, _cacheProvider, _defaultRadix);
}

/**
Expand All @@ -409,7 +416,7 @@ public BaseSettings with(PolymorphicTypeValidator v) {
return new BaseSettings(_classIntrospector, _annotationIntrospector,
_propertyNamingStrategy, _enumNamingStrategy, _typeFactory,
_typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
_timeZone, _defaultBase64, v, _accessorNaming, _cacheProvider);
_timeZone, _defaultBase64, v, _accessorNaming, _cacheProvider, _defaultRadix);
}

/**
Expand All @@ -425,7 +432,23 @@ public BaseSettings with(CacheProvider cacheProvider) {
return new BaseSettings(_classIntrospector, _annotationIntrospector,
_propertyNamingStrategy, _enumNamingStrategy, _typeFactory,
_typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
_timeZone, _defaultBase64, _typeValidator, _accessorNaming, cacheProvider);
_timeZone, _defaultBase64, _typeValidator, _accessorNaming, cacheProvider, _defaultRadix);
}

/**
* Fluent factory for constructing a new instance with provided default radix.
*
* @return a new instance with provided defaultRadix.
* @since 2.21
*/
public BaseSettings withRadix(int defaultRadix) {
if (defaultRadix == _defaultRadix) {
return this;
}
return new BaseSettings(_classIntrospector, _annotationIntrospector,
_propertyNamingStrategy, _enumNamingStrategy, _typeFactory,
_typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
_timeZone, _defaultBase64, _typeValidator, _accessorNaming, _cacheProvider, defaultRadix);
}

/*
Expand Down Expand Up @@ -503,6 +526,10 @@ public boolean hasExplicitTimeZone() {
public Base64Variant getBase64Variant() {
return _defaultBase64;
}

public int getRadix() {
return _defaultRadix;
}

/**
* @since 2.16
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,14 @@ public Base64Variant getBase64Variant() {
return _base.getBase64Variant();
}

/**
* Method called during deserialization if a Number needs to be decoded from
* String. Default version just returns radix 10.
*/
public int getRadix() {
return _base.getRadix();
}

/**
* Method for accessing per-instance shared (baseline/default)
* attribute values; these are used as the basis for per-call
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,12 @@ public T withRootName(String rootName) {
*/
public abstract T with(SubtypeResolver str);

/**
* Method for constructing and returning a new instance with different
* radix to use.
*/
public abstract T withRadix(int radix);

/**
* Method for constructing and returning a new instance with different
* view to use.
Expand Down Expand Up @@ -933,8 +939,8 @@ public PropertyName findRootName(Class<?> rawRootType) {
public final Class<?> findMixInClassFor(Class<?> cls) {
return _mixIns.findMixInClassFor(cls);
}

// Not really relevant here (should not get called)

@Override
public MixInResolver copy() {
throw new UnsupportedOperationException();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.fasterxml.jackson.databind.deser.std;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;

import java.io.IOException;
import java.math.BigInteger;

/**
* Deserializer used to deserialize string that represent number under in specific radix (base).
*/
public class FromStringWithRadixToNumberDeserializer
extends StdDeserializer<Number> {
private final int radix;

public FromStringWithRadixToNumberDeserializer(StdDeserializer<?> src, int radix) {
super(src);
this.radix = radix;
}

@Override
public Number deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
Class<?> handledType = handledType();

if (p.currentToken() != JsonToken.VALUE_STRING) {
ctxt.reportInputMismatch(handledType,
"Read something other than string when deserializing a value using FromStringWithRadixToNumberDeserializer.");
}

String text = p.getText();

if (handledType.equals(BigInteger.class)) {
return new BigInteger(text, radix);
} else if (handledType.equals(byte.class) || handledType.equals(Byte.class)) {
return Byte.parseByte(text, radix);
} else if (handledType.equals(short.class) || handledType.equals(Short.class)) {
return Short.parseShort(text, radix);
} else if (handledType.equals(int.class) || handledType.equals(Integer.class)) {
return Integer.parseInt(text, radix);
} else if (handledType.equals(long.class) || handledType.equals(Long.class)) {
return Long.parseLong(text, radix);
} else {
ctxt.reportInputMismatch(handledType,
"Trying to deserialize a non-whole number with NumberToStringWithRadixSerializer");
Copy link
Author

@tiger9800 tiger9800 Sep 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered explicitly throwing here, but this seemed like a more common way to indicate an error.

return null;//should not reach here
}
}
}
Loading