using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Collections.ObjectModel;
using System.Text.Json.Serialization;
public class RoundingJsonConverter : RoundingJsonConverterBase
public RoundingJsonConverter(int numberOfDecimals) => NumberOfDecimals = (numberOfDecimals < 0 || numberOfDecimals > 28 ? throw new ArgumentOutOfRangeException(nameof(numberOfDecimals)) : numberOfDecimals);
protected override int NumberOfDecimals { get; }
public class RoundingTo2DigitsJsonConverter : RoundingJsonConverterBase
protected override int NumberOfDecimals { get; } = 2;
public class RoundingTo6DigitsJsonConverter : RoundingJsonConverterBase
protected override int NumberOfDecimals { get; } = 6;
public abstract class RoundingJsonConverterBase : JsonConverter<object>
protected abstract int NumberOfDecimals { get; }
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
typeToConvert = Nullable.GetUnderlyingType(typeToConvert) ?? typeToConvert;
if (typeToConvert == typeof(decimal))
return reader.GetDecimal();
else if (typeToConvert == typeof(double))
return reader.GetDouble();
else if (typeToConvert == typeof(float))
return (float)reader.GetDouble();
throw new NotImplementedException();
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
writer.WriteNumberValue(Math.Round(d, Math.Min(15, NumberOfDecimals)));
writer.WriteNumberValue(Math.Round(d, NumberOfDecimals));
writer.WriteNumberValue((decimal)Math.Round((decimal)f, NumberOfDecimals));
throw new NotImplementedException();
public override bool CanConvert(Type typeToConvert)
typeToConvert = Nullable.GetUnderlyingType(typeToConvert) ?? typeToConvert;
return typeToConvert == typeof(double) || typeToConvert == typeof(decimal) || typeToConvert == typeof(float);
public double d { get; set; }
public double? nd { get; set; }
public float f { get; set; }
public float? nf { get; set; }
public decimal dc { get; set; }
public decimal ndc { get; set; }
[JsonConverter(typeof(RoundingTo6DigitsJsonConverter))]
public decimal? SixDecimalPlaceValue { get; set; }
public static void Test()
Test(4, 1.8888888888888888888888888888888888M);
foreach (var i in Enumerable.Range(0, 28))
Assert.Throws<ArgumentOutOfRangeException>(() => Test(29));
static void Test(int numberOfDigits)
Test(numberOfDigits, 1.1111111111111111111111111111111111M);
static void Test(int numberOfDigits, decimal value)
SixDecimalPlaceValue = value,
var options = new JsonSerializerOptions
Converters = { new RoundingJsonConverter(numberOfDigits) },
var json = JsonSerializer.Serialize(model, options);
public static void Main()
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine("System.Text.Json version: " + typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");
public static string GetNetCoreVersion()
var assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
var assemblyPath = assembly.Location.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
return assemblyPath[netCoreAppIndex + 1];