Skip to main content

NewtonsoftParityDoubleConverter

Namespace: Meshmakers.Octo.Runtime.Contracts.Serialization

System.Text.Json converters that match Newtonsoft's whole-number formatting for Double, Single, and Decimal: an integral value renders with a trailing .0 (e.g. 0.0 rather than STJ's default 0).

public sealed class NewtonsoftParityDoubleConverter : JsonConverter<Double>

Inheritance Object → JsonConverter → JsonConverter<Double> → NewtonsoftParityDoubleConverter

Remarks:

Without these converters STJ writes double 0.0 as the JSON literal 0, which on round-trip through RtAttributesConverter + JsonScalar looks like an integer and gets boxed as Int64. That widening then surfaces in MongoDB as a BsonInt64 attribute where Newtonsoft's path stored a BsonDouble — observable as the (quantity=0, BsonInt64) rows in RtEntity_EnergyCommunityEnergyQuantity on the octogrid tenant after the STJ migration.

Newtonsoft's JsonConvert.ToString(double 0.0) emits "0.0" (its writer always appends a fractional digit to disambiguate from integers). These converters reproduce that rule. Non-integral, non-special values fall through to the built-in number writer / reader, so precision and round-tripping behaviour for normal fractional values is unchanged.

NumberHandling flag preservation. Custom converters bypass STJ's built-in number handling, so the converter must re-implement behaviour for the flags RtSystemTextJsonSerializer sets:

— NaN / Infinity / -Infinity written as JSON strings on write, accepted as strings on read.

— numeric JSON strings ("42", "3.14") accepted on read in addition to bare numbers.

Implementation uses Utf8Formatter with a stackalloc Span<byte> for the integral path — no heap allocations per value.

Verified by Sdk.Common.PipelineParityTests.AttributeRoundTripClrTypeParityTests: with these converters registered, double-zero and double-one round-trip as Double matching Newtonsoft. decimal and float sources reach the boundary but still lose source-CLR-type fidelity (JSON has no decimal-vs-double or float-vs-double marker) — those are documented in AttributeValueParityCorpus.IrreducibleDivergences; the integral-.0 emission still prevents the long-widening for those whole-number cases.

Properties

HandleNull

public bool HandleNull { get; }

Property Value

Boolean

Type

public Type Type { get; }

Property Value

Type

Constructors

NewtonsoftParityDoubleConverter()

public NewtonsoftParityDoubleConverter()

Methods

Read(out Utf8JsonReader, Type, JsonSerializerOptions)

public double Read(out Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)

Parameters

reader Utf8JsonReader

typeToConvert Type

options JsonSerializerOptions

Returns

Double

Write(Utf8JsonWriter, Double, JsonSerializerOptions)

public void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options)

Parameters

writer Utf8JsonWriter

value Double

options JsonSerializerOptions

HasFractionalOrExponent(ReadOnlySpan<Byte>)

internal static bool HasFractionalOrExponent(ReadOnlySpan<byte> utf8Number)

Parameters

utf8Number ReadOnlySpan<Byte>

Returns

Boolean