<div dir="ltr"><p>Sure, Volkan.</p>
<p>Let me explain with a sample use case (I also have the source code available on GitHub at <a href="https://github.com/sathishk/parse-number">https://github.com/sathishk/parse-number</a> ).</p>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">When developing <strong>REST APIs or Data Engineering applications</strong>, we often deal with <strong>untyped JSON documents</strong>, which are loaded into Java as <code>Map<String, Object></code> (for JSON objects) or <code>List<Object></code> (for JSON arrays). Typically, JSON parsers convert these into Java objects.</blockquote>
<p>Consider this example:</p>
<pre><code class="gmail-language-java">Map<String, Number> originalData = Map.of(
    "byteValue", (byte) 123,
    "shortValue", (short) 1234
);
</code></pre>
<p>This would be represented as the following JSON:</p>
<pre><code class="gmail-language-json">{
  "shortValue": 1234,
  "byteValue": 123
}
</code></pre>
<p>Now, when we <strong>deserialize</strong> this JSON using a parser like <a href="https://www.baeldung.com/java-json-maps-comparison#1-using-jackson">Jackson</a>:</p>
<pre><code class="gmail-language-java">Map<String, Object> deserializedData = new JsonReader().getJacksonMap(jsonString);
</code></pre>
<p>The expectation is that <b><font color="#38761d">numeric values should be preserved in their smallest possible type</font></b> (<code>byte</code> or <code>short</code>). However, <strong><font color="#cc0000">Jackson (and most JSON parsers) default to <code>Integer</code> for whole numbers</font></strong> because they rely on <code>Integer.parseInt(String)</code>, which is a safe fallback for most cases.</p>
<h3>Why <code>Number.parseNumber(String)</code>?</h3>
<p>If we had a <strong>generalized <code>Number.parseNumber(String)</code> method</strong>, it could intelligently determine the most memory-efficient number type based on the value range. This would help reduce unnecessary memory usage, especially when handling large datasets in <strong>high-performance applications</strong>.</p>
<p>Would love to hear your thoughts on this approach!</p>
<p>Thanks & Regards,<br>
Sathish Kumar Thiyagarajan</p></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Mon, 31 Mar 2025 at 14:56, Volkan Yazıcı <<a href="mailto:volkan@yazi.ci">volkan@yazi.ci</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr">Sathish, would you mind elaborating on your use case a bit more, please? That is, I am not able to see where and how you want to leverage `Number#parseNumber(String)` in the `testNumberMemoryUsage()` method. Example: <i>"I have this test method verifying this behavior of that application. Given a `Number#parseNumber(String)`, I can improve this as follows."</i></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, Mar 28, 2025 at 6:31 PM Sathish Kumar Thiyagarajan <<a href="mailto:sathishkumar.thiyagarajan@gmail.com" target="_blank">sathishkumar.thiyagarajan@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><p>Dear Core-Libs Dev Team,</p>
<p><strong>Note:</strong> I have now subscribed to the mailing list and am resending this message as advised.</p>
<p>I would like to propose an improvement to the JDK: a <strong>generalized <code>Number.parseNumber(String)</code> method</strong>.</p>
<h3>Motivation</h3>
<p>Java provides multiple number types (<code>byte</code>, <code>short</code>, <code>int</code>, <code>long</code>, etc.), and developers typically choose them based on memory considerations. Currently, Java offers <code>String</code> to <code>Number</code> conversions using concrete classes:</p>
<ul>
<li>
<p><code>Long.parseLong(String)</code></p>
</li>
<li>
<p><code>Integer.parseInt(String)</code></p>
</li>
<li>
<p><code>Short.parseShort(String)</code>, etc.</p>
</li>
</ul>
<p>While these are useful, Java lacks a <strong>generalized method</strong> that returns the most memory-efficient <code>Number</code> representation based on the input, like:</p>
<pre><code>Number.parseNumber(String numberAsText);
</code></pre>
<h3>Use Case: JSON Serialization</h3>
<p>This would be particularly useful in cases like <strong>JSON serialization in REST APIs (Using <a href="https://github.com/FasterXML/jackson" target="_blank">Jackson</a>)</strong>, where number types are often altered during serialization/deserialization. Consider the following test case:</p>
<pre><code>@Test
void testNumberMemoryUsage() throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    Map<String, Object> numbersObject = Map.of("aShort", (short) 1234, "aFloat", (float) 1.33);

    final String jsonText = mapper.writeValueAsString(numbersObject);
    Map<String, Object> parsedJsonObject = mapper.readValue(jsonText, new TypeReference<>() {});

    // Expected: Short.class | Actual: Integer.class
    assertEquals(Short.class, parsedJsonObject.get("aShort").getClass());

    // Expected: Float.class | Actual: Double.class
    assertEquals(Float.class, parsedJsonObject.get("aFloat").getClass());
}
</code></pre>
<h3>Reference Implementation</h3>
<p>Here’s a rough implementation to illustrate the idea:</p>
<pre><code>private static Number parseNumber(final String numberStr) {
    try {
        if (numberStr.contains(".")) {
            double doubleValue = Double.parseDouble(numberStr);
            return (doubleValue >= -Float.MAX_VALUE && doubleValue <= Float.MAX_VALUE) ? 
                   (float) doubleValue : doubleValue;
        } else {
            long longValue = Long.parseLong(numberStr);
            if (longValue >= Byte.MIN_VALUE && longValue <= Byte.MAX_VALUE) {
                return (byte) longValue;
            } else if (longValue >= Short.MIN_VALUE && longValue <= Short.MAX_VALUE) {
                return (short) longValue;
            } else if (longValue >= Integer.MIN_VALUE && longValue <= Integer.MAX_VALUE) {
                return (int) longValue;
            } else {
                return longValue;
            }
        }
    } catch (NumberFormatException e) {
        return parseBigNumber(numberStr);
    }
}

private static Number parseBigNumber(final String numberStr) {
    try {
        return new BigInteger(numberStr); // Try BigInteger first
    } catch (NumberFormatException e) {
        // Only create BigDecimal if BigInteger fails
        BigDecimal bd = new BigDecimal(numberStr);
        try {
            // Convert to BigInteger if there's no fraction
            return bd.toBigIntegerExact();
        } catch (ArithmeticException ex) {
            return bd; // If it's a decimal, return BigDecimal
        }
    }
}
</code></pre>
<p>Would love to hear your thoughts on this proposal. Appreciate your feedback and guidance!</p>
<p>Thanks & Regards,<br>
Sathish Kumar Thiyagarajan</p></div>
</blockquote></div>
</blockquote></div>