Type Coerce¶
What it does¶
Converts a scalar field's JSON type — string, integer, number, or boolean — between whatever the hub and spoke schemas each declare at the same path.
When to use it¶
The same field, same path, same meaning, is typed differently between versions — e.g. a priority field that was a string in one version and became an integer in another (a common cleanup once an API's real constraints become clear).
Always lossless for whole values within float64 precision
Canonically-formatted whole values that fit in the IEEE-754 binary64 mantissa (up to 2⁵³) round-trip exactly in both directions. Integers beyond that range cannot be represented exactly as JSON numbers. A value that genuinely can't be parsed as the target type (a non-numeric string coerced to a number) is a runtime conversion error, not a lossiness concern — it means the input object doesn't actually match its own declared schema.
Fractional numbers written into an integer destination follow onFractionalInteger (default Error). Truncate and Round require acknowledgeLossy: true because they discard precision.
Truncation matrix¶
JSON numbers decode as float64 in Go. The interesting cases are therefore "what happens when that float lands on a typed destination":
| Source value | Dest schema | onFractionalInteger |
Result |
|---|---|---|---|
5 / "5" / 5.0 |
integer | any | 5 |
1.7 / "1.7" |
integer | Error (default) |
conversion error |
1.7 / "1.7" |
integer | Truncate |
1 (lossy) |
1.7 / "1.7" |
integer | Round |
2 (lossy) |
1.7 |
number | n/a | 1.7 (unchanged) |
5 |
string | n/a | "5" |
"true" |
boolean | n/a | true |
"nope" |
integer | any | conversion error (unparseable) |
onFractionalInteger has no effect when the destination is string, number, or boolean.
Example¶
The hub's priority (an integer) is a string on the v2 spoke:
Rule¶
- strategy: TypeCoerce
typeCoerce:
path: spec.priority
# onFractionalInteger: Error # default; Truncate and Round are lossy
path is the same dotted path on both sides — typeCoerce only makes sense when the field itself isn't renamed, just its type.
Objects¶
Numbers are formatted canonically (no unnecessary trailing zeros or exponent notation) so a round trip through both directions always reproduces the exact original value.