将JsonConverter转换为System.Text.Json以支持多个基元类型并可为null



我正在尝试将此Newtonsoft.Json.JsonConverter转换为System.Text.Json。但是,我只能使用单个基元类型,比如double,即使在那里我也无法将转换器应用于nullable(double?(。如何将其转换为支持null和所有数字格式(float、double(。

Newtonsoft.Json

public class DecimalRoundingJsonConverter : JsonConverter
{
private readonly int _numberOfDecimals;
public DecimalRoundingJsonConverter() : this(6)
{
}
public DecimalRoundingJsonConverter(int numberOfDecimals)
{
_numberOfDecimals = numberOfDecimals;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
double input = 0;
if (value is decimal)
{
var d = (decimal)value;
input = Convert.ToDouble(d);
}
else if (value is float)
{
var d = (float)value;
input = Convert.ToDouble(d);
}
else
{
input = (double)value;
}
var rounded = Math.Round(input, _numberOfDecimals);
writer.WriteValue((decimal)rounded);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanRead
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(decimal);
}
}

System.Text.Json(基本(

public class DecimalRoundingJsonConverter : JsonConverter<double>
{
private readonly int _numberOfDecimals = 6;
public override double Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
throw new NotImplementedException();
}
public override void Write(
Utf8JsonWriter writer,
double dvalue,
JsonSerializerOptions options)
{
double input = (double)dvalue;

var rounded = Math.Round(input, _numberOfDecimals);
writer.WriteStringValue(rounded.ToString());
}
}

您可以创建一个转换器,该转换器应用于所有floatdoubledecimal值,以及相同的null值,方法是创建一个JsonConverter<object>并重写JsonConverter<object>.CanConvert(Type)以仅对六个相关类型返回true。

以下操作完成任务:

public class RoundingJsonConverter : RoundingJsonConverterBase
{
// The converter works for float, double & decimal.  Max number of decimals for double is 15, for decimal is 28, so throw an exception of numberOfDecimals > 28.
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)
{
switch (value)
{
case double d:
writer.WriteNumberValue(Math.Round(d, Math.Min(15, NumberOfDecimals)));
break;
case decimal d:
writer.WriteNumberValue(Math.Round(d, NumberOfDecimals));
break;
case float f:
writer.WriteNumberValue((decimal)Math.Round((decimal)f, NumberOfDecimals));
break;
default:
throw new NotImplementedException();
}
}

public override bool CanConvert(Type typeToConvert)
{
typeToConvert = Nullable.GetUnderlyingType(typeToConvert) ?? typeToConvert;
return typeToConvert == typeof(double) || typeToConvert == typeof(decimal) || typeToConvert == typeof(float);
}           
}

注:

  • System.Text.Json没有等效于Newtonsoft的JsonConverter.CanRead,因此必须实现Read()Write()

  • 将转换器添加到JsonSerializerOptions.Converters时,请使用DecimalRoundingJsonConverter并传递所需位数作为构造函数参数,例如:

    var options = new JsonSerializerOptions
    {
    Converters = { new RoundingJsonConverter(6) },
    };
    

    但是,如果您通过属性应用转换器,Microsoft不允许传递转换器参数(请参阅此处进行确认(,因此您需要为每个所需数字创建特定的转换器类型,例如

    public class RoundingTo2DigitsJsonConverter : RoundingJsonConverterBase
    {
    protected override int NumberOfDecimals { get; } = 2;
    }
    public class RoundingTo6DigitsJsonConverter : RoundingJsonConverterBase
    {
    protected override int NumberOfDecimals { get; } = 6;
    }
    

    然后应用,例如:

    [JsonConverter(typeof(RoundingTo6DigitsJsonConverter))]
    public decimal? SixDecimalPlaceValue { get; set; }
    
  • CCD_ 11可用于获得诸如decimal?double?之类的可为null类型的底层类型。

  • 除非JsonConverter<T>.HandleNull被重写以返回true,否则对于可为null的null值永远不会调用JsonConverter<T>.Write()

在这里演示小提琴。

相关内容

最新更新