Skip to content

fix: Preserve integral Number type in toJsonTree for byte/short/int#2998

Open
daguimu wants to merge 1 commit intogoogle:mainfrom
daguimu:fix/integer-to-long-type-issue2680
Open

fix: Preserve integral Number type in toJsonTree for byte/short/int#2998
daguimu wants to merge 1 commit intogoogle:mainfrom
daguimu:fix/integer-to-long-type-issue2680

Conversation

@daguimu
Copy link
Copy Markdown

@daguimu daguimu commented Mar 26, 2026

Problem

When using toJsonTree with byte, short, or int types, the resulting JsonPrimitive stores a Long instead of the expected Byte, Short, or Integer. This is a regression introduced by commit 3e3266c.

Root Cause

The BYTE, SHORT, and INTEGER type adapters call out.value(value.byteValue()), out.value(value.shortValue()), and out.value(value.intValue()) respectively. Since JsonWriter only has value(long) (no value(int)), Java auto-widens the primitive to long. In JsonTreeWriter, value(long) creates new JsonPrimitive(long), storing a Long regardless of the original type.

Fix

Cast the narrowed primitive to Number before passing to value(). This triggers autoboxing to Byte/Short/Integer and causes Java to resolve value(Number) instead of value(long). JsonTreeWriter.value(Number) preserves the actual Number type in the JsonPrimitive. For regular JsonWriter, value(Number) produces identical text output.

Tests Added

  • testToJsonTreePreservesIntegralType — verifies that toJsonTree preserves:
    • Byte type for byte/Byte.class
    • Short type for short/Short.class
    • Integer type for int/Integer.class
    • Long type for long/Long.class (unchanged, regression guard)
    • Correct target type after narrowing conversion (e.g., DoubleInteger, LongByte)

Impact

Only affects code paths using JsonTreeWriter (i.e., toJsonTree). Regular JSON string serialization via toJson is unaffected since value(Number) and value(long) produce the same text output. All 4587 existing tests pass.

Fixes #2680

@google-cla
Copy link
Copy Markdown

google-cla bot commented Mar 26, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

The BYTE, SHORT, and INTEGER type adapters' write methods called
value(byteValue()), value(shortValue()), and value(intValue())
respectively. Since JsonWriter only has value(long), Java auto-widened
the primitive to long, causing JsonTreeWriter.value(long) to store a
Long in the JsonPrimitive instead of the original Byte/Short/Integer.

Fix by casting the narrowed primitive to Number, which triggers
autoboxing to the correct type and resolves to value(Number) instead
of value(long).

Fixes google#2680
@daguimu daguimu force-pushed the fix/integer-to-long-type-issue2680 branch from eef4366 to 34378c3 Compare March 26, 2026 01:45
@Marcono1234
Copy link
Copy Markdown
Contributor

Thanks for the PR, for what it's worth, there is already an existing (though by now slightly outdated) PR: #2814

I guess the maintainers will decide which one they prefer. So feel free to keep your PR here open.

@eamonnmcmanus
Copy link
Copy Markdown
Member

Thanks for the PR, for what it's worth, there is already an existing (though by now slightly outdated) PR: #2814

I guess the maintainers will decide which one they prefer. So feel free to keep your PR here open.

Sorry, I'm not sure why we didn't get around to merging #2814. If that one and this are functionally identical (are they?) then we might go with this one just because #2814 would have to be brought up to date.

I'm testing it against all of Google's internal tests, which will take a little while.

@Marcono1234
Copy link
Copy Markdown
Contributor

#2814 also tried to keep reusing the existing Number object if it already has the correct type, e.g. use Integer as is instead of doing (Number) integer.intValue(). Not sure if that provides any performance improvement though. So I guess it can be omitted.

@Marcono1234
Copy link
Copy Markdown
Contributor

The other alternative would be to revert 3e3266c (as mentioned also in #2814); maybe that change was not worth it, and would have only mattered for rare corner cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TypeAdapterFactory INTEGER_FACTORY will change INTEGER type to LONG type

3 participants