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
Type
public Type Type { get; }
Property Value
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
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>