Skip to content

fix: widen YAML-loaded numbers to the declared numeric field type - #3068

Merged
tastybento merged 1 commit into
developfrom
fix/yaml-numeric-widening
Aug 15, 2026
Merged

fix: widen YAML-loaded numbers to the declared numeric field type#3068
tastybento merged 1 commit into
developfrom
fix/yaml-numeric-widening

Conversation

@tastybento

Copy link
Copy Markdown
Member

What this fixes

YAML types a number by how it is written: 20 loads as Integer and 20.0 as Double, regardless of the declared field type. A double config field written without a decimal point reached the setter as an Integer; inside a collection, generic erasure hid the mismatch until the first read threw ClassCastException — a long way from the cause.

Changes

  • Replaced the ad-hoc Integer→Long promotion in deserialize() with a widen() helper covering Long, Double and Float targets (boxed and primitive).
  • Widening only: a decimal written against an int or long field is a config mistake, so it is left alone to fail visibly in the deserialize log rather than being silently truncated. The long branch is gated on integral sources for this reason. Double→float is the one accepted narrowing, since YAML always types decimals as Double and a float field must still load.
  • The "float" switch arm in deserializeValue now uses Number.floatValue() instead of casting to Doublewiden() delivers a boxed Float there, and the old cast would have thrown ClassCastException for every primitive float field.

Tests

New unit tests cover Integer→Double/Float/Long widening, the refusal to narrow Double→Integer and Double→Long, non-numeric targets being left untouched, and a loadObject round-trip on a primitive float field written both with and without a decimal point (the case that exercises the switch arm).

🤖 Generated with Claude Code

https://claude.ai/code/session_01XkVFZoe4hB2Req885z5ea7

YAML types a number by how it is written: 20 loads as Integer and 20.0
as Double, regardless of the declared field type. A double field written
without a decimal point reached the setter as an Integer; inside a
collection, generic erasure hid the mismatch until the first read threw
ClassCastException far from the cause.

Replace the ad-hoc Integer-to-Long promotion in deserialize() with a
widen() helper covering Long, Double and Float targets. Widening only:
a decimal written against an int or long field is a config mistake, so
it is left alone to fail visibly rather than silently truncated
(the long branch is gated on integral sources).

The float switch arm in deserializeValue now accepts the boxed Float
that widen() produces via Number.floatValue() instead of casting to
Double, which would throw ClassCastException for every primitive float
field.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XkVFZoe4hB2Req885z5ea7
@sonarqubecloud

Copy link
Copy Markdown

@tastybento
tastybento merged commit 8e0e736 into develop Aug 15, 2026
3 checks passed
@tastybento
tastybento deleted the fix/yaml-numeric-widening branch August 15, 2026 02:01
@tastybento tastybento mentioned this pull request Aug 16, 2026
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.

1 participant