Skip to content
Open
18 changes: 18 additions & 0 deletions src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -1896,6 +1896,24 @@ public ObjectMapper setDefaultPropertyInclusion(JsonInclude.Value incl) {
return this;
}

/**
* Method for setting default alternative radix that applies to integral types for serialization
* and deserialization of such types as strings.
* This configuration override is applied for all integral properties for which there are no per-type
* or per-property overrides (via annotations or config overrides).
*<p>
* NOTE: in Jackson 3.x all configuration goes through {@code ObjectMapper} builders,
* see {@link com.fasterxml.jackson.databind.cfg.MapperBuilder},
* and this method will be removed from 3.0.
*
* @since 2.21
*/
public ObjectMapper setDefaultFormat(String radix) {
_configOverrides.setDefaultRadix(radix);
return this;
}


/**
* Short-cut for:
*<pre>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ public BaseSettings(ClassIntrospector ci, AnnotationIntrospector ai,
_defaultBase64 = defaultBase64;
_typeValidator = ptv;
_accessorNaming = accNaming;
_cacheProvider = cacheProvider;
_cacheProvider = cacheProvider;;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class ConfigOverrides
implements java.io.Serializable
{
private static final long serialVersionUID = 1L;
private static final String DEFAULT_RADIX = "10";

/**
* Per-type override definitions
Expand Down Expand Up @@ -55,6 +56,14 @@ public class ConfigOverrides
*/
protected Boolean _defaultLeniency;

/**
* Global default radix to apply to an integral type outputted as string. This has the lowest precedence out of all
* other methods of enforcing an alternative radix.
*
* @since 2.21
*/
protected String _defaultRadix;//TODO(Davyd Fridman): Change from string to int once JsonFormat has an actual radix field

/*
/**********************************************************************
/* Life cycle
Expand All @@ -67,13 +76,32 @@ public ConfigOverrides() {
JsonInclude.Value.empty(),
JsonSetter.Value.empty(),
VisibilityChecker.Std.defaultInstance(),
null, null
null, null, DEFAULT_RADIX
);
}

/**
* @since 2.21
*/
protected ConfigOverrides(Map<Class<?>, MutableConfigOverride> overrides,
JsonInclude.Value defIncl, JsonSetter.Value defSetter,
VisibilityChecker<?> defVisibility, Boolean defMergeable, Boolean defLeniency,
String defRadix)
{
_overrides = overrides;
_defaultInclusion = defIncl;
_defaultSetterInfo = defSetter;
_visibilityChecker = defVisibility;
_defaultMergeable = defMergeable;
_defaultLeniency = defLeniency;
_defaultRadix = defRadix;
}

/**
* @since 2.10
* @deprecated since 2.21
*/
@Deprecated
protected ConfigOverrides(Map<Class<?>, MutableConfigOverride> overrides,
JsonInclude.Value defIncl, JsonSetter.Value defSetter,
VisibilityChecker<?> defVisibility, Boolean defMergeable, Boolean defLeniency)
Expand Down Expand Up @@ -197,6 +225,13 @@ public VisibilityChecker<?> getDefaultVisibility() {
return _visibilityChecker;
}

/**
* @since 2.21
*/
public String getDefaultRadix() {
return _defaultRadix;
}

/**
* @since 2.9
*/
Expand Down Expand Up @@ -232,6 +267,13 @@ public void setDefaultVisibility(VisibilityChecker<?> v) {
_visibilityChecker = v;
}

/**
* @since 2.21
*/
public void setDefaultRadix(String v) {
this._defaultRadix = v;
}

/*
/**********************************************************************
/* Helper methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.function.Consumer;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
Expand Down Expand Up @@ -747,6 +748,20 @@ public B defaultPropertyInclusion(JsonInclude.Value incl) {
return _this();
}

/**
* Method for configured default radix to use for serialization/deserialization of integral types as strings.
*
* @param radix Default radix to use on integral properties
*
* @return This builder instance to allow call chaining
*
* @since 2.11
*/
public B defaultFormat(String radix) {
_mapper.setDefaultFormat(radix);
return _this();
}

/*
/**********************************************************************
/* Configuring Mix-ins
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,14 @@ public JsonInclude.Value getDefaultInclusion(Class<?> baseType,
return result;
}

/**
* Accessor for default radix to apply to integral types when serializing them as string.
* The radix obtained from this accessor should have the lowest precedence.
*
* @since 2.21
*/
public abstract String getDefaultRadix();

/**
* Accessor for default format settings to use for serialization (and, to a degree
* deserialization), considering baseline settings and per-type defaults
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,11 @@ public final JsonInclude.Value getDefaultInclusion(Class<?> baseType,
return def.withOverrides(v);
}

@Override
public String getDefaultRadix() {
return _configOverrides.getDefaultRadix();
}

@Override
public final JsonFormat.Value getDefaultPropertyFormat(Class<?> type) {
return _configOverrides.findFormatDefaults(type);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
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 for a string that represents a number in specific radix (base).
*
* @since 2.21
*/
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