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