Issue
Summary
When Cloud Controller proxies JSON payloads that contain decimal numbers (e.g. 0.1) it may change the JSON type from number to string (e.g. "0.1").
This is unexpected for clients and breaks "type-preserving" pass-through semantics for broker-provided JSON.
Scenario
Endpoint: GET /v3/service_instances/:guid/parameters (managed service instance)
Observed behavior:
- Cloud Controller queries the service broker for parameters.
- Cloud Controller parses the returned JSON string.
- Decimal numbers are loaded as
BigDecimal.
- Cloud Controller serializes the
"parameters" field back to JSON.
- The response contains strings instead of JSON numbers.
Service broker returns:
{
"parameters": {
"key": 0.1
}
}
Cloud Controller responds with:
{
"parameters": {
"key": "0.1"
}
}
Expected behavior
Cloud Controller should ideally preserve JSON numeric types when proxying broker-provided JSON:
{
"parameters": {
"key": 0.1
}
}
Background / Root cause analysis
Cloud Controller uses Oj in Rails mode:
mode: :rails
Oj::Rails.optimize
1) Oj loads decimals as BigDecimal
Cloud Controller configures Oj with bigdecimal_load: :bigdecimal, meaning JSON numbers with decimals/exponents are parsed into BigDecimal.
Reference:
2) Rails converts BigDecimal to String before encoding JSON
In Rails mode, the JSON pipeline typically invokes as_json before encoding. ActiveSupport’s BigDecimal#as_json returns a String, so the numeric value is already stringified before the JSON encoder runs.
This results in a response where decimal values are encoded as JSON strings.
Impact / Affected payloads
Cloud Controller passes through various objects where types should ideally not change:
- Configuration parameters
- service instances and service bindings
- Credentials
- Catalog metadata
- service offerings and service plans
- Mount config
Possible solutions
Option 1: bigdecimal_load: :auto (parse many decimals as Float)
Switch parsing from bigdecimal_load: :bigdecimal to bigdecimal_load: :auto so that many "simple" decimal tokens are parsed as Float instead of BigDecimal. Those values would then typically be serialized as JSON numbers.
Major drawback (value preservation): this can change numeric values.
:auto uses a heuristic based largely on significant digits / "fits in float precision", but that is not the same as "exactly representable in IEEE-754 binary floating point". Many decimal fractions cannot be represented exactly as Float (e.g. values like 0.1), and near the precision boundary you can observe last-digit drift.
In other words, Option 1 may improve type preservation ("number stays number"), but it risks breaking value preservation, which is worse than a type change.
Option 2: Preserve values with BigDecimal, but encode BigDecimal as JSON numbers
Keep bigdecimal_load: :bigdecimal to ensure no Float conversion happens and numeric values remain exact within Cloud Controller.
Then make the JSON encoding pipeline emit BigDecimal as a JSON number instead of a string, by:
- Preventing Rails/ActiveSupport from converting
BigDecimal to String during as_json
- override
BigDecimal#as_json to return self
- Setting Oj
bigdecimal_as_decimal: true so Oj serializes BigDecimals as JSON numbers
Tradeoffs:
- This changes current Rails JSON semantics globally unless carefully scoped.
Option 3: Keep current behavior (BigDecimals encoded as JSON strings)
Leave the behavior as-is: parse decimals into BigDecimal, then emit them as JSON strings.
Advantages:
- Value preservation is guaranteed inside Cloud Controller (no Float conversion, no rounding drift).
- Maximum cross-language safety: Many consumers (especially JavaScript) parse JSON numbers as IEEE-754 floats. Emitting decimals as strings avoids accidental precision loss or rounding in clients, proxies, logs, or intermediate systems.
- Stable, explicit contract: A string strongly signals "treat as decimal, not float", and avoids subtle numeric bugs downstream.
- Lowest risk: no changes to global JSON parsing/encoding behavior.
Disadvantage:
- Not type-preserving for pass-through payloads: JSON numbers from brokers may become strings in CC responses (e.g.
0.1 → "0.1"), which can break clients expecting numeric JSON types.
Additional remark: lexical form cannot be guaranteed
Once Cloud Controller parses a JSON number token into a numeric value and later re-serializes it, it cannot guarantee the exact original spelling (dropping trailing zeros, changing exponent vs fixed notation, etc.). The goal can be type/value preservation, but not exact lexical round-tripping.
Issue
Summary
When Cloud Controller proxies JSON payloads that contain decimal numbers (e.g.
0.1) it may change the JSON type from number to string (e.g."0.1").This is unexpected for clients and breaks "type-preserving" pass-through semantics for broker-provided JSON.
Scenario
Endpoint:
GET /v3/service_instances/:guid/parameters(managed service instance)Observed behavior:
BigDecimal."parameters"field back to JSON.Service broker returns:
{ "parameters": { "key": 0.1 } }Cloud Controller responds with:
{ "parameters": { "key": "0.1" } }Expected behavior
Cloud Controller should ideally preserve JSON numeric types when proxying broker-provided JSON:
{ "parameters": { "key": 0.1 } }Background / Root cause analysis
Cloud Controller uses
Ojin Rails mode:mode: :railsOj::Rails.optimize1) Oj loads decimals as BigDecimal
Cloud Controller configures Oj with
bigdecimal_load: :bigdecimal, meaning JSON numbers with decimals/exponents are parsed intoBigDecimal.Reference:
2) Rails converts BigDecimal to String before encoding JSON
In Rails mode, the JSON pipeline typically invokes
as_jsonbefore encoding. ActiveSupport’sBigDecimal#as_jsonreturns a String, so the numeric value is already stringified before the JSON encoder runs.This results in a response where decimal values are encoded as JSON strings.
Impact / Affected payloads
Cloud Controller passes through various objects where types should ideally not change:
Possible solutions
Option 1:
bigdecimal_load: :auto(parse many decimals as Float)Switch parsing from
bigdecimal_load: :bigdecimaltobigdecimal_load: :autoso that many "simple" decimal tokens are parsed asFloatinstead ofBigDecimal. Those values would then typically be serialized as JSON numbers.Major drawback (value preservation): this can change numeric values.
:autouses a heuristic based largely on significant digits / "fits in float precision", but that is not the same as "exactly representable in IEEE-754 binary floating point". Many decimal fractions cannot be represented exactly as Float (e.g. values like0.1), and near the precision boundary you can observe last-digit drift.In other words, Option 1 may improve type preservation ("number stays number"), but it risks breaking value preservation, which is worse than a type change.
Option 2: Preserve values with BigDecimal, but encode BigDecimal as JSON numbers
Keep
bigdecimal_load: :bigdecimalto ensure no Float conversion happens and numeric values remain exact within Cloud Controller.Then make the JSON encoding pipeline emit
BigDecimalas a JSON number instead of a string, by:BigDecimalto String duringas_jsonBigDecimal#as_jsonto returnselfbigdecimal_as_decimal: trueso Oj serializes BigDecimals as JSON numbersTradeoffs:
Option 3: Keep current behavior (BigDecimals encoded as JSON strings)
Leave the behavior as-is: parse decimals into
BigDecimal, then emit them as JSON strings.Advantages:
Disadvantage:
0.1→"0.1"), which can break clients expecting numeric JSON types.Additional remark: lexical form cannot be guaranteed
Once Cloud Controller parses a JSON number token into a numeric value and later re-serializes it, it cannot guarantee the exact original spelling (dropping trailing zeros, changing exponent vs fixed notation, etc.). The goal can be type/value preservation, but not exact lexical round-tripping.