将 JsonConverter 转换为 System.Text.Json 以支持多种基本类型和可为空
Convert JsonConverter to System.Text.Json to support multiple primitive types and nullable
我正在尝试将此 Newtonsoft.Json.JsonConverter 转换为 System.Text.Json。但是,我只能使用一个原始类型,比如 double,即使在那里我也无法在可空(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());
}
}
您可以通过创建 JsonConverter<object>
和仅对六种相关类型将 JsonConverter<object>.CanConvert(Type)
覆盖为 return 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 不允许传递转换器参数(请参阅 here 进行确认)因此您需要为每个所需的位数创建一个特定的转换器类型, 例如
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; }
Nullable.GetUnderlyingType(Type)
可用于获取可空类型的基础类型,例如 decimal?
或 double?
.
JsonConverter<T>.Write()
永远不会为可空值的 null
值调用,除非 JsonConverter<T>.HandleNull
被覆盖为 return true
.
演示 fiddle here.
我正在尝试将此 Newtonsoft.Json.JsonConverter 转换为 System.Text.Json。但是,我只能使用一个原始类型,比如 double,即使在那里我也无法在可空(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());
}
}
您可以通过创建 JsonConverter<object>
和仅对六种相关类型将 JsonConverter<object>.CanConvert(Type)
覆盖为 return 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 不允许传递转换器参数(请参阅 here 进行确认)因此您需要为每个所需的位数创建一个特定的转换器类型, 例如
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; }
Nullable.GetUnderlyingType(Type)
可用于获取可空类型的基础类型,例如decimal?
或double?
.JsonConverter<T>.Write()
永远不会为可空值的null
值调用,除非JsonConverter<T>.HandleNull
被覆盖为 returntrue
.
演示 fiddle here.