diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java index 102607f0d3..10cda9efc6 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java +++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java @@ -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). + *

+ * 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: *

diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/BaseSettings.java b/src/main/java/com/fasterxml/jackson/databind/cfg/BaseSettings.java
index 76f2d9fe1a..e2e66cb524 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/BaseSettings.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/BaseSettings.java
@@ -184,7 +184,7 @@ public BaseSettings(ClassIntrospector ci, AnnotationIntrospector ai,
         _defaultBase64 = defaultBase64;
         _typeValidator = ptv;
         _accessorNaming = accNaming;
-        _cacheProvider = cacheProvider;
+        _cacheProvider = cacheProvider;;
     }
 
     /**
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverrides.java b/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverrides.java
index 4961de2386..4fed2bc044 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverrides.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigOverrides.java
@@ -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
@@ -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
@@ -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, 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, MutableConfigOverride> overrides,
             JsonInclude.Value defIncl, JsonSetter.Value defSetter,
             VisibilityChecker defVisibility, Boolean defMergeable, Boolean defLeniency)
@@ -197,6 +225,13 @@ public VisibilityChecker getDefaultVisibility() {
         return _visibilityChecker;
     }
 
+    /**
+     * @since 2.21
+     */
+    public String getDefaultRadix() {
+        return _defaultRadix;
+    }
+
     /**
      * @since 2.9
      */
@@ -232,6 +267,13 @@ public void setDefaultVisibility(VisibilityChecker v) {
         _visibilityChecker = v;
     }
 
+    /**
+     * @since 2.21
+     */
+    public void setDefaultRadix(String v) {
+        this._defaultRadix = v;
+    }
+
     /*
     /**********************************************************************
     /* Helper methods
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperBuilder.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperBuilder.java
index cbfe4277d2..cc9f7fa613 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperBuilder.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperBuilder.java
@@ -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;
@@ -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
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java
index 061224e1e5..97a861601c 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java
@@ -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
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java
index 139253571a..b0fcdb3456 100644
--- a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java
@@ -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);
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringWithRadixToNumberDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringWithRadixToNumberDeserializer.java
new file mode 100644
index 0000000000..bafcb69f12
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringWithRadixToNumberDeserializer.java
@@ -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 {
+    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");
+            return null;//should not reach here
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java
index 029e36e1ef..4ba35ea868 100644
--- a/src/main/java/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java
@@ -5,13 +5,16 @@
 import java.math.BigInteger;
 import java.util.HashSet;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.core.io.NumberInput;
 import com.fasterxml.jackson.databind.*;
 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
 import com.fasterxml.jackson.databind.cfg.CoercionAction;
 import com.fasterxml.jackson.databind.cfg.CoercionInputShape;
+import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.ser.std.NumberToStringWithRadixSerializer;
 import com.fasterxml.jackson.databind.type.LogicalType;
 import com.fasterxml.jackson.databind.util.AccessPattern;
 import com.fasterxml.jackson.databind.util.ClassUtil;
@@ -26,6 +29,8 @@
 public class NumberDeserializers
 {
     private final static HashSet _classNames = new HashSet();
+    private final static int DEFAULT_RADIX = 10;
+
     static {
         // note: can skip primitive types; other ways to check them:
         Class[] numberTypes = new Class[] {
@@ -250,7 +255,7 @@ public Boolean deserializeWithType(JsonParser p, DeserializationContext ctxt,
 
     @JacksonStdImpl
     public static class ByteDeserializer
-        extends PrimitiveOrWrapperDeserializer
+        extends PrimitiveOrWrapperDeserializer implements ContextualDeserializer
     {
         private static final long serialVersionUID = 1L;
 
@@ -274,6 +279,12 @@ public Byte deserialize(JsonParser p, DeserializationContext ctxt) throws IOExce
             return _parseByte(p, ctxt);
         }
 
+        @Override
+        public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property)
+                throws JsonMappingException {
+            return _createRadixStringDeserializer(this, ctxt, property);
+        }
+
         protected Byte _parseByte(JsonParser p, DeserializationContext ctxt)
                 throws IOException
         {
@@ -342,7 +353,7 @@ protected Byte _parseByte(JsonParser p, DeserializationContext ctxt)
 
     @JacksonStdImpl
     public static class ShortDeserializer
-        extends PrimitiveOrWrapperDeserializer
+        extends PrimitiveOrWrapperDeserializer implements ContextualDeserializer
     {
         private static final long serialVersionUID = 1L;
 
@@ -367,6 +378,12 @@ public Short deserialize(JsonParser p, DeserializationContext ctxt)
             return _parseShort(p, ctxt);
         }
 
+        @Override
+        public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property)
+                throws JsonMappingException {
+            return _createRadixStringDeserializer(this, ctxt, property);
+        }
+
         protected Short _parseShort(JsonParser p, DeserializationContext ctxt)
                 throws IOException
         {
@@ -517,8 +534,7 @@ public Character deserialize(JsonParser p, DeserializationContext ctxt)
 
     @JacksonStdImpl
     public final static class IntegerDeserializer
-        extends PrimitiveOrWrapperDeserializer
-    {
+        extends PrimitiveOrWrapperDeserializer implements ContextualDeserializer {
         private static final long serialVersionUID = 1L;
 
         final static IntegerDeserializer primitiveInstance = new IntegerDeserializer(Integer.TYPE, 0);
@@ -557,11 +573,17 @@ public Integer deserializeWithType(JsonParser p, DeserializationContext ctxt,
             }
             return _parseInteger(p, ctxt, Integer.class);
         }
+
+        @Override
+        public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property)
+                throws JsonMappingException {
+            return _createRadixStringDeserializer(this, ctxt, property);
+        }
     }
 
     @JacksonStdImpl
     public final static class LongDeserializer
-        extends PrimitiveOrWrapperDeserializer
+        extends PrimitiveOrWrapperDeserializer implements ContextualDeserializer
     {
         private static final long serialVersionUID = 1L;
 
@@ -586,6 +608,12 @@ public Long deserialize(JsonParser p, DeserializationContext ctxt) throws IOExce
             }
             return _parseLong(p, ctxt, Long.class);
         }
+
+        @Override
+        public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property)
+                throws JsonMappingException {
+            return _createRadixStringDeserializer(this, ctxt, property);
+        }
     }
 
     @JacksonStdImpl
@@ -936,7 +964,7 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
     @SuppressWarnings("serial")
     @JacksonStdImpl
     public static class BigIntegerDeserializer
-        extends StdScalarDeserializer
+        extends StdScalarDeserializer implements ContextualDeserializer
     {
         public final static BigIntegerDeserializer instance = new BigIntegerDeserializer();
 
@@ -1011,6 +1039,12 @@ public BigInteger deserialize(JsonParser p, DeserializationContext ctxt) throws
             return (BigInteger) ctxt.handleWeirdStringValue(_valueClass, text,
                     "not a valid representation");
         }
+
+        @Override
+        public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property)
+                throws JsonMappingException {
+            return _createRadixStringDeserializer(this, ctxt, property);
+        }
     }
 
     @SuppressWarnings("serial")
@@ -1089,4 +1123,42 @@ public BigDecimal deserialize(JsonParser p, DeserializationContext ctxt)
                     "not a valid representation");
         }
     }
+
+    /**
+     * Method used to create a string deserializer for a number.
+     * If configuration is set properly, we create an alternative radix serializer {@link NumberToStringWithRadixSerializer}.
+     *
+     * @since 2.21
+     */
+    private static StdDeserializer _createRadixStringDeserializer(StdScalarDeserializer initialDeser,
+                    DeserializationContext ctxt, BeanProperty property)
+    {
+        JsonFormat.Value format = initialDeser.findFormatOverrides(ctxt, property, initialDeser.handledType());
+
+        if (format == null || format.getShape() != JsonFormat.Shape.STRING) {
+            return initialDeser;
+        }
+
+        if (isSerializeWithRadixOverride(format)) {
+            int radix = Integer.parseInt(format.getPattern());
+            return new FromStringWithRadixToNumberDeserializer(initialDeser, radix);
+        }
+
+        return initialDeser;
+    }
+
+    /**
+     * Check if we have a proper {@link JsonFormat} annotation for serializing a number
+     * using an alternative radix specified in the annotation.
+     */
+    private static boolean isSerializeWithRadixOverride(JsonFormat.Value format) {
+        String pattern = format.getPattern();
+        boolean isInteger = pattern.chars().allMatch(Character::isDigit);
+        if (!isInteger || pattern.isEmpty()) {
+            return false;
+        }
+
+        int radix = Integer.parseInt(pattern);
+        return radix != DEFAULT_RADIX;
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java b/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java
index 315bb76906..fbe786be56 100644
--- a/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBase.java
@@ -71,6 +71,7 @@ public final JsonFormat.Value findFormatOverrides(AnnotationIntrospector intr) {
     @Override
     public JsonFormat.Value findPropertyFormat(MapperConfig config, Class baseType)
     {
+        JsonFormat.Value v0 = EMPTY_FORMAT.withPattern(config.getDefaultRadix());//TODO(Davyd Fridman): change to withRadix
         JsonFormat.Value v1 = config.getDefaultPropertyFormat(baseType);
         JsonFormat.Value v2 = null;
         AnnotationIntrospector intr = config.getAnnotationIntrospector();
@@ -80,10 +81,18 @@ public JsonFormat.Value findPropertyFormat(MapperConfig config, Class base
                 v2 = intr.findFormat(member);
             }
         }
-        if (v1 == null) {
-            return (v2 == null) ? EMPTY_FORMAT : v2;
+
+        JsonFormat.Value formatValue = EMPTY_FORMAT;
+        if (v0 != null) {
+            formatValue = formatValue.withOverrides(v0);
+        }
+        if (v1 != null) {
+            formatValue = formatValue.withOverrides(v1);
+        }
+        if (v2 != null) {
+            formatValue = formatValue.withOverrides(v2);
         }
-        return (v2 == null) ? v1 : v1.withOverrides(v2);
+        return formatValue;
     }
 
     @Override
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java
index 56c2391dae..6e970f88e5 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializer.java
@@ -37,6 +37,8 @@ public class NumberSerializer
 
     protected final boolean _isInt;
 
+    protected static final int DEFAULT_RADIX = 10;
+
     /**
      * @since 2.5
      */
@@ -58,7 +60,8 @@ public JsonSerializer createContextual(SerializerProvider prov,
                 if (((Class) handledType()) == BigDecimal.class) {
                     return bigDecimalAsStringSerializer();
                 }
-                return ToStringSerializer.instance;
+                return createStringSerializer(prov, format, _isInt);
+
             default:
             }
         }
@@ -114,6 +117,35 @@ public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType t
         }
     }
 
+    /**
+     * Method used to create a string serializer for a number. If the number is integer, and configuration is set properly,
+     * we create an alternative radix serializer {@link NumberToStringWithRadixSerializer}.
+     *
+     * @since 2.21
+     */
+    public static ToStringSerializerBase createStringSerializer(SerializerProvider prov, JsonFormat.Value format, boolean isInt) {
+        if (isInt && isSerializeWithRadixOverride(format)) {
+            int radix = Integer.parseInt(format.getPattern());
+            return new NumberToStringWithRadixSerializer(radix);
+        }
+        return ToStringSerializer.instance;
+    }
+
+    /**
+     * Check if we have a proper {@link JsonFormat} annotation for serializing a number
+     * using an alternative radix specified in the annotation.
+     */
+    private static boolean isSerializeWithRadixOverride(JsonFormat.Value format) {
+        String pattern = format.getPattern();
+        boolean isInteger = pattern.chars().allMatch(Character::isDigit);
+        if (!isInteger || pattern.isEmpty()) {
+            return false;
+        }
+
+        int radix = Integer.parseInt(pattern);
+        return radix != DEFAULT_RADIX;
+    }
+
     /**
      * @since 2.10
      */
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java
index be7e956a2f..f5a36006a9 100644
--- a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java
@@ -106,7 +106,7 @@ public JsonSerializer createContextual(SerializerProvider prov,
                     if (((Class) handledType()) == BigDecimal.class) {
                         return NumberSerializer.bigDecimalAsStringSerializer();
                     }
-                    return ToStringSerializer.instance;
+                    return NumberSerializer.createStringSerializer(prov, format, _isInt);
                 default:
                 }
             }
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberToStringWithRadixSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberToStringWithRadixSerializer.java
new file mode 100644
index 0000000000..edef76bc23
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberToStringWithRadixSerializer.java
@@ -0,0 +1,67 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+/**
+ * Serializer used to convert numbers into a representation for a specified radix (base) and serialize
+ * the representation as string.
+ *
+ * @since 2.21
+ */
+@JacksonStdImpl
+public class NumberToStringWithRadixSerializer extends ToStringSerializerBase {
+    private final int radix;
+
+    public NumberToStringWithRadixSerializer(int radix) { super(Object.class);
+        this.radix = radix;
+    }
+
+    public NumberToStringWithRadixSerializer(Class handledType, int radix) {
+        super(handledType);
+        this.radix = radix;
+    }
+
+    @Override
+    public boolean isEmpty(SerializerProvider prov, Object value) {
+        return false;
+    }
+
+    @Override
+    public void serialize(Object value, JsonGenerator gen, SerializerProvider provider)
+            throws IOException
+    {
+        if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
+            String errorMsg = String.format("To use a custom radix for string serialization, use radix within [%d, %d]", Character.MIN_RADIX, Character.MAX_RADIX);
+            provider.reportBadDefinition(handledType(), errorMsg);
+        }
+
+        String text = "";
+        if (value instanceof BigInteger) {
+            BigInteger bigIntegerValue = (BigInteger) value;
+            text = bigIntegerValue.toString(radix);
+        } else if (value instanceof Byte
+                || value instanceof Short
+                || value instanceof Integer
+                || value instanceof Long) {
+            long longValue = ((Number) value).longValue();
+            text = Long.toString(longValue, radix);
+        } else {
+            provider.reportBadDefinition(handledType(),
+                    "Trying to serialize a non-whole number with NumberToStringWithRadixSerializer");
+        }
+
+        gen.writeString(text);
+
+    }
+
+    @Override
+    public String valueToString(Object value) {
+        // should never be called
+        throw new IllegalStateException();
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/SerializeUsingJDKTest.java b/src/test/java/com/fasterxml/jackson/databind/SerializeUsingJDKTest.java
index d607fc4f76..f8943f39c7 100644
--- a/src/test/java/com/fasterxml/jackson/databind/SerializeUsingJDKTest.java
+++ b/src/test/java/com/fasterxml/jackson/databind/SerializeUsingJDKTest.java
@@ -238,6 +238,8 @@ public void testTypeFactory() throws Exception
     public void testObjectReaderSerializationWithPolymorphism()
         throws Exception
     {
+        Properties props = System.getProperties();
+        props.setProperty("sun.io.serialization.extendedDebugInfo", "true");
         Class[] classes = new Class[] {
             FooClass.class,
             FooDeduction.class,
diff --git a/src/test/java/com/fasterxml/jackson/databind/format/DifferentRadixNumberFormatTest.java b/src/test/java/com/fasterxml/jackson/databind/format/DifferentRadixNumberFormatTest.java
new file mode 100644
index 0000000000..4ab8783d05
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/format/DifferentRadixNumberFormatTest.java
@@ -0,0 +1,216 @@
+package com.fasterxml.jackson.databind.format;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.testutil.DatabindTestUtil;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigInteger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public class DifferentRadixNumberFormatTest extends DatabindTestUtil {
+
+    private static final String HEX_RADIX = "16";
+    public static final String BINARY_RADIX = "2";
+
+    private static class IntegerWrapper {
+        public Integer value;
+
+        public IntegerWrapper() {}
+        public IntegerWrapper(Integer v) { value = v; }
+    }
+
+    private static class IntWrapper {
+        public int value;
+
+        public IntWrapper() {}
+        public IntWrapper(int v) { value = v; }
+    }
+
+    private static class AnnotatedMethodIntWrapper {
+        private int value;
+
+        public AnnotatedMethodIntWrapper() {
+        }
+        public AnnotatedMethodIntWrapper(int v) {
+            value = v;
+        }
+
+        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = HEX_RADIX)
+        public int getValue() {
+            return value;
+        }
+    }
+
+    private static class IncorrectlyAnnotatedMethodIntWrapper {
+        private int value;
+
+        public IncorrectlyAnnotatedMethodIntWrapper() {
+        }
+        public IncorrectlyAnnotatedMethodIntWrapper(int v) {
+            value = v;
+        }
+
+        @JsonFormat(shape = JsonFormat.Shape.STRING)
+        public int getValue() {
+            return value;
+        }
+    }
+
+    private static class AllIntegralTypeWrapper {
+        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = BINARY_RADIX)
+        public byte byteValue;
+        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = BINARY_RADIX)
+        public Byte ByteValue;
+
+        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = BINARY_RADIX)
+        public short shortValue;
+        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = BINARY_RADIX)
+        public Short ShortValue;
+
+        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = BINARY_RADIX)
+        public int intValue;
+        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = BINARY_RADIX)
+        public Integer IntegerValue;
+
+        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = BINARY_RADIX)
+        public long longValue;
+        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = BINARY_RADIX)
+        public Long LongValue;
+
+        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = BINARY_RADIX)
+        public BigInteger bigInteger;
+
+        public AllIntegralTypeWrapper() {
+        }
+
+        public AllIntegralTypeWrapper(byte byteValue, Byte ByteValue, short shortValue, Short ShortValue, int intValue,
+                                      Integer IntegerValue, long longValue, Long LongValue, BigInteger bigInteger) {
+            this.byteValue = byteValue;
+            this.ByteValue = ByteValue;
+            this.shortValue = shortValue;
+            this.ShortValue = ShortValue;
+            this.intValue = intValue;
+            this.IntegerValue = IntegerValue;
+            this.longValue = longValue;
+            this.LongValue = LongValue;
+            this.bigInteger = bigInteger;
+        }
+    }
+
+    @Test
+    void testIntegerSerializedAsHexString()
+            throws JsonProcessingException {
+        ObjectMapper mapper = newJsonMapper();
+        mapper.configOverride(Integer.class).setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING).withPattern(HEX_RADIX));
+        IntegerWrapper initialIntegerWrapper = new IntegerWrapper(10);
+        String json = mapper.writeValueAsString(initialIntegerWrapper);
+        String expectedJson = "{'value':'a'}";
+
+        assertEquals(a2q(expectedJson), json);
+
+        IntegerWrapper readBackIntegerWrapper = mapper.readValue(a2q(expectedJson), IntegerWrapper.class);
+
+        assertNotNull(readBackIntegerWrapper);
+        assertEquals(initialIntegerWrapper.value, readBackIntegerWrapper.value);
+    }
+
+
+    @Test
+    void testIntSerializedAsHexString()
+            throws JsonProcessingException {
+        ObjectMapper mapper = newJsonMapper();
+        mapper.configOverride(int.class)
+              .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING).withPattern(HEX_RADIX));
+        IntWrapper intialIntWrapper = new IntWrapper(10);
+        String expectedJson = "{'value':'a'}";
+
+        String json = mapper.writeValueAsString(intialIntWrapper);
+
+        assertEquals(a2q(expectedJson), json);
+
+        IntWrapper readBackIntWrapper = mapper.readValue(a2q(expectedJson), IntWrapper.class);
+
+        assertNotNull(readBackIntWrapper);
+        assertEquals(intialIntWrapper.value, readBackIntWrapper.value);
+
+    }
+
+    @Test
+    void testAnnotatedAccessorSerializedAsHexString()
+            throws JsonProcessingException {
+        ObjectMapper mapper = newJsonMapper();
+        AnnotatedMethodIntWrapper initialIntWrapper = new AnnotatedMethodIntWrapper(10);
+        String expectedJson = "{'value':'a'}";
+
+        String json = mapper.writeValueAsString(initialIntWrapper);
+
+        assertEquals(a2q(expectedJson), json);
+
+        AnnotatedMethodIntWrapper readBackIntWrapper = mapper.readValue(a2q(expectedJson), AnnotatedMethodIntWrapper.class);
+
+        assertNotNull(readBackIntWrapper);
+        assertEquals(initialIntWrapper.value, readBackIntWrapper.value);
+    }
+
+    @Test
+    void testAnnotatedAccessorWithoutPatternDoesNotThrow()
+            throws JsonProcessingException {
+        ObjectMapper mapper = newJsonMapper();
+        IncorrectlyAnnotatedMethodIntWrapper initialIntWrapper = new IncorrectlyAnnotatedMethodIntWrapper(10);
+        String expectedJson = "{'value':'10'}";
+
+        String json = mapper.writeValueAsString(initialIntWrapper);
+
+        assertEquals(a2q(expectedJson), json);
+    }
+
+    @Test
+    void testUsingDefaultConfigOverrideRadixToSerializeAsHexString()
+            throws JsonProcessingException {
+        ObjectMapper mapper = newJsonMapper();
+        mapper.configOverride(Integer.class)
+              .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING));
+        mapper.setDefaultFormat(HEX_RADIX);
+        IntegerWrapper intialIntegerWrapper = new IntegerWrapper(10);
+        String expectedJson = "{'value':'a'}";
+
+        String json = mapper.writeValueAsString(intialIntegerWrapper);
+
+        assertEquals(a2q(expectedJson), json);
+
+        IntegerWrapper readBackIntegerWrapper = mapper.readValue(a2q(expectedJson), IntegerWrapper.class);
+
+        assertNotNull(readBackIntegerWrapper);
+        assertEquals(intialIntegerWrapper.value, readBackIntegerWrapper.value);
+    }
+
+    @Test
+    void testAllIntegralTypesGetSerializedAsBinary()
+            throws JsonProcessingException {
+        ObjectMapper mapper = newJsonMapper();
+        AllIntegralTypeWrapper initialIntegralTypeWrapper = new AllIntegralTypeWrapper((byte) 1,
+                (byte) 2, (short) 3, (short) 4, 5, 6, 7L, 8L, new BigInteger("9"));
+        String expectedJson = "{'byteValue':'1','ByteValue':'10','shortValue':'11','ShortValue':'100','intValue':'101','IntegerValue':'110','longValue':'111','LongValue':'1000','bigInteger':'1001'}";
+
+        String json = mapper.writeValueAsString(initialIntegralTypeWrapper);
+
+        assertEquals(a2q(expectedJson), json);
+
+        AllIntegralTypeWrapper readbackIntegralTypeWrapper = mapper.readValue(a2q(expectedJson), AllIntegralTypeWrapper.class);
+
+        assertNotNull(readbackIntegralTypeWrapper);
+        assertEquals(initialIntegralTypeWrapper.byteValue, readbackIntegralTypeWrapper.byteValue);
+        assertEquals(initialIntegralTypeWrapper.ByteValue, readbackIntegralTypeWrapper.ByteValue);
+        assertEquals(initialIntegralTypeWrapper.shortValue, readbackIntegralTypeWrapper.shortValue);
+        assertEquals(initialIntegralTypeWrapper.ShortValue, readbackIntegralTypeWrapper.ShortValue);
+        assertEquals(initialIntegralTypeWrapper.intValue, readbackIntegralTypeWrapper.intValue);
+        assertEquals(initialIntegralTypeWrapper.IntegerValue, readbackIntegralTypeWrapper.IntegerValue);
+        assertEquals(initialIntegralTypeWrapper.longValue, readbackIntegralTypeWrapper.longValue);
+        assertEquals(initialIntegralTypeWrapper.LongValue, readbackIntegralTypeWrapper.LongValue);
+        assertEquals(initialIntegralTypeWrapper.bigInteger, readbackIntegralTypeWrapper.bigInteger);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBaseTest.java b/src/test/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBaseTest.java
new file mode 100644
index 0000000000..f7fe25d607
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/ConcreteBeanPropertyBaseTest.java
@@ -0,0 +1,181 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.AnnotationIntrospector;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.PropertyMetadata;
+import com.fasterxml.jackson.databind.PropertyName;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Member;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+//TODO: Once mockito is updated to include Premain-Class in its MANIFEST.MF, we need to add -javaagent:/${m2directory}/.m2/repository/org/mockito/mockito-core/${mockit-version}/$33{mockit-version}.jar
+class ConcreteBeanPropertyBaseTest {
+
+    private static final class TestConcreteBeanPropertyBase extends ConcreteBeanPropertyBase {
+
+        TestConcreteBeanPropertyBase(PropertyMetadata md) {
+            super(md);
+        }
+
+        @Override
+        public String getName() {
+            return "";
+        }
+
+        @Override
+        public PropertyName getFullName() {
+            return null;
+        }
+
+        @Override
+        public JavaType getType() {
+            return null;
+        }
+
+        @Override
+        public PropertyName getWrapperName() {
+            return null;
+        }
+
+        @Override
+        public  A getAnnotation(Class acls) {
+            return null;
+        }
+
+        @Override
+        public  A getContextAnnotation(Class acls) {
+            return null;
+        }
+
+        @Override
+        public AnnotatedMember getMember() {
+            return new TestAnnotatedMember(null, null);
+        }
+
+        @Override
+        public void depositSchemaProperty(JsonObjectFormatVisitor objectVisitor, SerializerProvider provider)
+                throws JsonMappingException {
+
+        }
+    }
+
+    private static final class TestAnnotatedMember extends AnnotatedMember {
+
+        TestAnnotatedMember(TypeResolutionContext ctxt, AnnotationMap annotations) {
+            super(ctxt, annotations);
+        }
+
+        @Override
+        public Annotated withAnnotations(AnnotationMap fallback) {
+            return null;
+        }
+
+        @Override
+        public Class getDeclaringClass() {
+            return null;
+        }
+
+        @Override
+        public Member getMember() {
+            return null;
+        }
+
+        @Override
+        public void setValue(Object pojo, Object value)
+                throws UnsupportedOperationException, IllegalArgumentException {
+
+        }
+
+        @Override
+        public Object getValue(Object pojo)
+                throws UnsupportedOperationException, IllegalArgumentException {
+            return null;
+        }
+
+        @Override
+        public AnnotatedElement getAnnotated() {
+            return null;
+        }
+
+        @Override
+        protected int getModifiers() {
+            return 0;
+        }
+
+        @Override
+        public String getName() {
+            return "";
+        }
+
+        @Override
+        public JavaType getType() {
+            return null;
+        }
+
+        @Override
+        public Class getRawType() {
+            return null;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return 0;
+        }
+
+        @Override
+        public String toString() {
+            return "";
+        }
+    }
+
+    private TestConcreteBeanPropertyBase testConcreteBeanProperty;
+    private Class someType;
+    private MapperConfig mapperConfig;
+    private AnnotationIntrospector annotationIntrospector;
+
+    @BeforeEach
+    void setUp() {
+        mapperConfig = mock(MapperConfig.class);
+        testConcreteBeanProperty =  new TestConcreteBeanPropertyBase(
+                PropertyMetadata.STD_REQUIRED);
+        annotationIntrospector = mock(AnnotationIntrospector.class);
+        when(mapperConfig.getAnnotationIntrospector()).thenReturn(annotationIntrospector);
+        someType = Class.class;
+    }
+
+    @Test
+    void testFormatPrecedenceIsFollowed() {
+        String lowestPrecedenceFormat = "Low Precedence";
+        JsonFormat.Value midPrecedenceFormat = new JsonFormat.Value("Mid Precedence", null,
+                (String) null, null, null, null);
+        JsonFormat.Value highestPrecedence = new JsonFormat.Value("High Precedence", null,
+                (String) null, null, null, null);
+        when(mapperConfig.getDefaultRadix()).thenReturn(lowestPrecedenceFormat);
+        when(mapperConfig.getDefaultPropertyFormat(any())).thenReturn(midPrecedenceFormat);
+        when(annotationIntrospector.findFormat(any())).thenReturn(highestPrecedence);
+
+        JsonFormat.Value resultFormat = testConcreteBeanProperty.findPropertyFormat(mapperConfig, someType);
+
+
+        assertEquals(highestPrecedence.getPattern(), resultFormat.getPattern());
+    }
+}
\ No newline at end of file