System.Text.Json:使用自动转换反序列化 JSON
System.Text.Json: Deserialize JSON with automatic casting
使用 .Net Core 3 的新 System.Text.Json JsonSerializer,如何自动转换类型(例如 int 到 string 和 string 到 int)?例如,这会引发异常,因为 JSON 中的 id
是数字,而 C# 的 Product.Id
需要字符串:
public class HomeController : Controller
{
public IActionResult Index()
{
var json = @"{""id"":1,""name"":""Foo""}";
var o = JsonSerializer.Deserialize<Product>(json, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
});
return View();
}
}
public class Product
{
public string Id { get; set; }
public string Name { get; set; }
}
Newtonsoft 的 Json.Net 处理得很好。如果您在 C# 期待一个字符串时传递一个数值(反之亦然),那么一切都按预期进行了反序列化。如果您无法控制作为 JSON 传递的类型格式,您将如何使用 System.Text.Json 处理此问题?
别担心。只需将 属性 添加到 class 即可 return 您想要的项目的类型。
public class Product
{
public int Id { get; set; }
public string IdString
{
get
{
return Id.ToString();
}
}
public string Name { get; set; }
}
编辑:您可以使用 JsonNumberHandlingAttribute
并且它在 1 行中正确处理所有内容,无需编写任何代码:
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
public class HomeController : Controller
....
原回答:
新的 System.Text.Json
api 公开了一个 JsonConverter
api 允许我们根据需要转换类型。
例如,我们可以创建一个通用的 number
到 string
转换器:
public class AutoNumberToStringConverter : JsonConverter<object>
{
public override bool CanConvert(Type typeToConvert)
{
return typeof(string) == typeToConvert;
}
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if(reader.TokenType == JsonTokenType.Number) {
return reader.TryGetInt64(out long l) ?
l.ToString():
reader.GetDouble().ToString();
}
if(reader.TokenType == JsonTokenType.String) {
return reader.GetString();
}
using(JsonDocument document = JsonDocument.ParseValue(ref reader)){
return document.RootElement.Clone().ToString();
}
}
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
{
writer.WriteStringValue( value.ToString());
}
}
使用MVC/Razor页面时,我们可以在启动时注册这个转换器:
services.AddControllersWithViews().AddJsonOptions(opts => {
opts.JsonSerializerOptions.PropertyNameCaseInsensitive= true;
opts.JsonSerializerOptions.Converters.Insert(0, new AutoNumberToStringConverter());
});
然后 MVC/Razor 将自动处理类型转换。
或者如果你喜欢手动控制serialization/deserialization:
var opts = new JsonSerializerOptions {
PropertyNameCaseInsensitive = true,
};
opts.Converters.Add(new AutoNumberToStringConverter());
var o = JsonSerializer.Deserialize<Product>(json,opts) ;
以类似的方式,您可以启用字符串到数字类型的转换,如下所示:
public class AutoStringToNumberConverter : JsonConverter<object>
{
public override bool CanConvert(Type typeToConvert)
{
// see
switch (Type.GetTypeCode(typeToConvert))
{
case TypeCode.Byte:
case TypeCode.SByte:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.Decimal:
case TypeCode.Double:
case TypeCode.Single:
return true;
default:
return false;
}
}
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if(reader.TokenType == JsonTokenType.String) {
var s = reader.GetString() ;
return int.TryParse(s,out var i) ?
i :
(double.TryParse(s, out var d) ?
d :
throw new Exception($"unable to parse {s} to number")
);
}
if(reader.TokenType == JsonTokenType.Number) {
return reader.TryGetInt64(out long l) ?
l:
reader.GetDouble();
}
using(JsonDocument document = JsonDocument.ParseValue(ref reader)){
throw new Exception($"unable to parse {document.RootElement.ToString()} to number");
}
}
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
{
var str = value.ToString(); // I don't want to write int/decimal/double/... for each case, so I just convert it to string . You might want to replace it with strong type version.
if(int.TryParse(str, out var i)){
writer.WriteNumberValue(i);
}
else if(double.TryParse(str, out var d)){
writer.WriteNumberValue(d);
}
else{
throw new Exception($"unable to parse {str} to number");
}
}
}
不幸的是,itminus 的例子没有用,这是我的变体。
public class AutoNumberToStringConverter : JsonConverter<string>
{
public override bool CanConvert(Type typeToConvert)
{
return typeof(string) == typeToConvert;
}
public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Number)
{
if (reader.TryGetInt64(out long number))
{
return number.ToString(CultureInfo.InvariantCulture);
}
if (reader.TryGetDouble(out var doubleNumber))
{
return doubleNumber.ToString(CultureInfo.InvariantCulture);
}
}
if (reader.TokenType == JsonTokenType.String)
{
return reader.GetString();
}
using var document = JsonDocument.ParseValue(ref reader);
return document.RootElement.Clone().ToString();
}
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
{
writer.WriteStringValue(value);
}
}
您可以使用 JsonNumberHandlingAttribute in your model class in order to specify how to treat number deserialization. The allowed options are specified in JsonNumberHandling 枚举。
用法示例:
public class Product
{
[JsonNumberHandling(JsonNumberHandling.WriteAsString)]
public string Id { get; set; }
public string Name { get; set; }
}
如果需要从string
序列化到int
,可以使用JsonNumberHandling.AllowReadingFromString
在选项中,将NumberHandling 属性设置为AllowReadingFromString
:
var o = JsonSerializer.Deserialize<Product>(json, new JsonSerializerOptions
{
// [...]
NumberHandling = JsonNumberHandling.AllowReadingFromString
});
截至撰写本文时,NumberHandling property 仅在 .NET 5.0 和 .NET 6.0 RC 中可用,我无法使用。
不幸的是,itminus 的字符串到数字转换器对我也不起作用。
所以我提出了另一个解决方案来处理不同的数字类型及其可为 null 的变体。我试图让代码尽可能干。
数字和可为空的数字类型
首先,用于字符串到数字和字符串到可空数字转换的主要泛型 classes:
public delegate T FromStringFunc<T>(string str);
public delegate T ReadingFunc<T>(ref Utf8JsonReader reader);
public delegate void WritingAction<T>(Utf8JsonWriter writer, T value);
public class StringToNumberConverter<T> : JsonConverter<T> where T : struct
{
protected ISet<TypeCode> AllowedTypeCodes { get; }
protected FromStringFunc<T> FromString { get; }
protected ReadingFunc<T> ReadValue { get; }
protected WritingAction<T> WriteValue { get; }
public StringToNumberConverter(ISet<TypeCode> allowedTypeCodes, FromStringFunc<T> fromString, ReadingFunc<T> read, WritingAction<T> write)
: base()
{
AllowedTypeCodes = allowedTypeCodes;
FromString = fromString;
ReadValue = read;
WriteValue = write;
}
public override bool CanConvert(Type typeToConvert)
{
return AllowedTypeCodes.Contains(Type.GetTypeCode(typeToConvert));
}
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.String)
{
var s = reader.GetString();
return FromString(s);
}
if (reader.TokenType == JsonTokenType.Number)
return ReadValue(ref reader);
using JsonDocument document = JsonDocument.ParseValue(ref reader);
throw new Exception($"unable to parse {document.RootElement} to number");
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
WriteValue(writer, value);
}
}
public class StringToNullableNumberConverter<T> : JsonConverter<T?> where T : struct
{
private readonly StringToNumberConverter<T> stringToNumber;
protected WritingAction<T> WriteValue { get; }
public StringToNullableNumberConverter(ISet<TypeCode> allowedTypeCodes, FromStringFunc<T> fromString, ReadingFunc<T> read, WritingAction<T> write)
: base()
{
stringToNumber = new StringToNumberConverter<T>(allowedTypeCodes, fromString, read, write);
WriteValue = write;
}
public override bool CanConvert(Type typeToConvert)
{
return stringToNumber.CanConvert(Nullable.GetUnderlyingType(typeToConvert) ?? typeToConvert);
}
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Null)
return null;
return stringToNumber.Read(ref reader, typeToConvert, options);
}
public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options)
{
if (!value.HasValue)
writer.WriteNullValue();
else
stringToNumber.Write(writer, value.Value, options);
}
}
然后是一个实用程序 class 来简化它们的使用。它包含不可概括的、类型精确的转换方法和设置:
static class StringToNumberUtil
{
public static readonly ISet<TypeCode> intCodes = new HashSet<TypeCode> { TypeCode.Byte, TypeCode.Int16, TypeCode.Int32 };
public static readonly ISet<TypeCode> longCodes = new HashSet<TypeCode> { TypeCode.Int64 };
public static readonly ISet<TypeCode> decimalCodes = new HashSet<TypeCode> { TypeCode.Decimal };
public static readonly ISet<TypeCode> doubleCodes = new HashSet<TypeCode> { TypeCode.Double };
public static int ParseInt(string s) => int.Parse(s, CultureInfo.InvariantCulture);
public static long ParseLong(string s) => long.Parse(s, CultureInfo.InvariantCulture);
public static decimal ParseDecimal(string s) => decimal.Parse(s, CultureInfo.InvariantCulture);
public static double ParseDouble(string s) => double.Parse(s, CultureInfo.InvariantCulture);
public static int ReadInt(ref Utf8JsonReader reader) => reader.GetInt32();
public static long ReadLong(ref Utf8JsonReader reader) => reader.GetInt64();
public static decimal ReadDecimal(ref Utf8JsonReader reader) => reader.GetDecimal();
public static double ReadDouble(ref Utf8JsonReader reader) => reader.GetDouble();
public static void WriteInt(Utf8JsonWriter writer, int value) => writer.WriteStringValue(value.ToString(CultureInfo.InvariantCulture));
public static void WriteLong(Utf8JsonWriter writer, long value) => writer.WriteStringValue(value.ToString(CultureInfo.InvariantCulture));
public static void WriteDecimal(Utf8JsonWriter writer, decimal value) => writer.WriteStringValue(value.ToString(CultureInfo.InvariantCulture));
public static void WriteDouble(Utf8JsonWriter writer, double value) => writer.WriteStringValue(value.ToString(CultureInfo.InvariantCulture));
}
最后,您可以为各个数字类型定义方便 classes...
public class StringToIntConverter : StringToNumberConverter<int>
{
public StringToIntConverter()
: base(StringToNumberUtil.intCodes, StringToNumberUtil.ParseInt, StringToNumberUtil.ReadInt, StringToNumberUtil.WriteInt)
{
}
}
public class StringToNullableIntConverter : StringToNullableNumberConverter<int>
{
public StringToNullableIntConverter()
: base(StringToNumberUtil.intCodes, StringToNumberUtil.ParseInt, StringToNumberUtil.ReadInt, StringToNumberUtil.WriteInt)
{
}
}
... 并像这样在 JsonSerializerOptions 中注册它们:
var options = new JsonSerializerOptions {
...
};
options.Converters.Add(new StringToIntConverter());
options.Converters.Add(new StringToNullableIntConverter());
...
(如果您愿意,也可以直接注册转换器。)
options.Converters.Add(new StringToNumberConverter<int>(StringToNumberUtil.intCodes, StringToNumberUtil.ParseInt, StringToNumberUtil.ReadInt, StringToNumberUtil.WriteInt));
options.Converters.Add(new StringToNullableNumberConverter<int>(StringToNumberUtil.intCodes, StringToNumberUtil.ParseInt, StringToNumberUtil.ReadInt, StringToNumberUtil.WriteInt));
应反序列化为枚举的数字
如果您的 JSON 包含字符串编码的数字属性,您可以添加它,其值具有可表示为枚举的预定义含义。
public class StringToIntEnumConverter<T> : JsonConverter<T> where T : struct, System.Enum
{
private StringToIntConverter stringToInt = new StringToIntConverter();
public override bool CanConvert(Type typeToConvert)
{
return typeToConvert == typeof(T);
}
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
int val = stringToInt.Read(ref reader, typeToConvert, options);
string underlyingValue = val.ToString(CultureInfo.InvariantCulture);
return (T)Enum.Parse(typeof(T), underlyingValue);
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
var number = Convert.ChangeType(value, Enum.GetUnderlyingType(typeof(T)), CultureInfo.InvariantCulture);
writer.WriteStringValue(number.ToString());
}
}
public class StringToNullableIntEnumConverter<T> : JsonConverter<T?> where T : struct, System.Enum
{
private StringToIntEnumConverter<T> stringToIntEnum = new StringToIntEnumConverter<T>();
public override bool CanConvert(Type typeToConvert)
{
return stringToIntEnum.CanConvert(Nullable.GetUnderlyingType(typeToConvert) ?? typeToConvert);
}
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Null)
return null;
return stringToIntEnum.Read(ref reader, typeToConvert, options);
}
public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options)
{
if (!value.HasValue)
{
writer.WriteNullValue();
return;
}
stringToIntEnum.Write(writer, value.Value, options);
}
}
JsonSerializerOptions 中的用法:
var options = new JsonSerializerOptions {
...
};
options.Converters.Add(new StringToIntEnumConverter<OrderFlags>());
options.Converters.Add(new StringToNullableIntEnumConverter<OrderFlags>());
...
使用 .Net Core 3 的新 System.Text.Json JsonSerializer,如何自动转换类型(例如 int 到 string 和 string 到 int)?例如,这会引发异常,因为 JSON 中的 id
是数字,而 C# 的 Product.Id
需要字符串:
public class HomeController : Controller
{
public IActionResult Index()
{
var json = @"{""id"":1,""name"":""Foo""}";
var o = JsonSerializer.Deserialize<Product>(json, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
});
return View();
}
}
public class Product
{
public string Id { get; set; }
public string Name { get; set; }
}
Newtonsoft 的 Json.Net 处理得很好。如果您在 C# 期待一个字符串时传递一个数值(反之亦然),那么一切都按预期进行了反序列化。如果您无法控制作为 JSON 传递的类型格式,您将如何使用 System.Text.Json 处理此问题?
别担心。只需将 属性 添加到 class 即可 return 您想要的项目的类型。
public class Product
{
public int Id { get; set; }
public string IdString
{
get
{
return Id.ToString();
}
}
public string Name { get; set; }
}
编辑:您可以使用 JsonNumberHandlingAttribute
并且它在 1 行中正确处理所有内容,无需编写任何代码:
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
public class HomeController : Controller
....
原回答:
新的
System.Text.Json
api 公开了一个JsonConverter
api 允许我们根据需要转换类型。例如,我们可以创建一个通用的
number
到string
转换器:public class AutoNumberToStringConverter : JsonConverter<object> { public override bool CanConvert(Type typeToConvert) { return typeof(string) == typeToConvert; } public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if(reader.TokenType == JsonTokenType.Number) { return reader.TryGetInt64(out long l) ? l.ToString(): reader.GetDouble().ToString(); } if(reader.TokenType == JsonTokenType.String) { return reader.GetString(); } using(JsonDocument document = JsonDocument.ParseValue(ref reader)){ return document.RootElement.Clone().ToString(); } } public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) { writer.WriteStringValue( value.ToString()); } }
使用MVC/Razor页面时,我们可以在启动时注册这个转换器:
services.AddControllersWithViews().AddJsonOptions(opts => { opts.JsonSerializerOptions.PropertyNameCaseInsensitive= true; opts.JsonSerializerOptions.Converters.Insert(0, new AutoNumberToStringConverter()); });
然后 MVC/Razor 将自动处理类型转换。
或者如果你喜欢手动控制serialization/deserialization:
var opts = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, }; opts.Converters.Add(new AutoNumberToStringConverter()); var o = JsonSerializer.Deserialize<Product>(json,opts) ;
以类似的方式,您可以启用字符串到数字类型的转换,如下所示:
public class AutoStringToNumberConverter : JsonConverter<object> { public override bool CanConvert(Type typeToConvert) { // see switch (Type.GetTypeCode(typeToConvert)) { case TypeCode.Byte: case TypeCode.SByte: case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.UInt64: case TypeCode.Int16: case TypeCode.Int32: case TypeCode.Int64: case TypeCode.Decimal: case TypeCode.Double: case TypeCode.Single: return true; default: return false; } } public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if(reader.TokenType == JsonTokenType.String) { var s = reader.GetString() ; return int.TryParse(s,out var i) ? i : (double.TryParse(s, out var d) ? d : throw new Exception($"unable to parse {s} to number") ); } if(reader.TokenType == JsonTokenType.Number) { return reader.TryGetInt64(out long l) ? l: reader.GetDouble(); } using(JsonDocument document = JsonDocument.ParseValue(ref reader)){ throw new Exception($"unable to parse {document.RootElement.ToString()} to number"); } } public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) { var str = value.ToString(); // I don't want to write int/decimal/double/... for each case, so I just convert it to string . You might want to replace it with strong type version. if(int.TryParse(str, out var i)){ writer.WriteNumberValue(i); } else if(double.TryParse(str, out var d)){ writer.WriteNumberValue(d); } else{ throw new Exception($"unable to parse {str} to number"); } } }
不幸的是,itminus 的例子没有用,这是我的变体。
public class AutoNumberToStringConverter : JsonConverter<string>
{
public override bool CanConvert(Type typeToConvert)
{
return typeof(string) == typeToConvert;
}
public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Number)
{
if (reader.TryGetInt64(out long number))
{
return number.ToString(CultureInfo.InvariantCulture);
}
if (reader.TryGetDouble(out var doubleNumber))
{
return doubleNumber.ToString(CultureInfo.InvariantCulture);
}
}
if (reader.TokenType == JsonTokenType.String)
{
return reader.GetString();
}
using var document = JsonDocument.ParseValue(ref reader);
return document.RootElement.Clone().ToString();
}
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
{
writer.WriteStringValue(value);
}
}
您可以使用 JsonNumberHandlingAttribute in your model class in order to specify how to treat number deserialization. The allowed options are specified in JsonNumberHandling 枚举。
用法示例:
public class Product
{
[JsonNumberHandling(JsonNumberHandling.WriteAsString)]
public string Id { get; set; }
public string Name { get; set; }
}
如果需要从string
序列化到int
,可以使用JsonNumberHandling.AllowReadingFromString
在选项中,将NumberHandling 属性设置为AllowReadingFromString
:
var o = JsonSerializer.Deserialize<Product>(json, new JsonSerializerOptions
{
// [...]
NumberHandling = JsonNumberHandling.AllowReadingFromString
});
截至撰写本文时,NumberHandling property 仅在 .NET 5.0 和 .NET 6.0 RC 中可用,我无法使用。 不幸的是,itminus 的字符串到数字转换器对我也不起作用。
所以我提出了另一个解决方案来处理不同的数字类型及其可为 null 的变体。我试图让代码尽可能干。
数字和可为空的数字类型
首先,用于字符串到数字和字符串到可空数字转换的主要泛型 classes:
public delegate T FromStringFunc<T>(string str);
public delegate T ReadingFunc<T>(ref Utf8JsonReader reader);
public delegate void WritingAction<T>(Utf8JsonWriter writer, T value);
public class StringToNumberConverter<T> : JsonConverter<T> where T : struct
{
protected ISet<TypeCode> AllowedTypeCodes { get; }
protected FromStringFunc<T> FromString { get; }
protected ReadingFunc<T> ReadValue { get; }
protected WritingAction<T> WriteValue { get; }
public StringToNumberConverter(ISet<TypeCode> allowedTypeCodes, FromStringFunc<T> fromString, ReadingFunc<T> read, WritingAction<T> write)
: base()
{
AllowedTypeCodes = allowedTypeCodes;
FromString = fromString;
ReadValue = read;
WriteValue = write;
}
public override bool CanConvert(Type typeToConvert)
{
return AllowedTypeCodes.Contains(Type.GetTypeCode(typeToConvert));
}
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.String)
{
var s = reader.GetString();
return FromString(s);
}
if (reader.TokenType == JsonTokenType.Number)
return ReadValue(ref reader);
using JsonDocument document = JsonDocument.ParseValue(ref reader);
throw new Exception($"unable to parse {document.RootElement} to number");
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
WriteValue(writer, value);
}
}
public class StringToNullableNumberConverter<T> : JsonConverter<T?> where T : struct
{
private readonly StringToNumberConverter<T> stringToNumber;
protected WritingAction<T> WriteValue { get; }
public StringToNullableNumberConverter(ISet<TypeCode> allowedTypeCodes, FromStringFunc<T> fromString, ReadingFunc<T> read, WritingAction<T> write)
: base()
{
stringToNumber = new StringToNumberConverter<T>(allowedTypeCodes, fromString, read, write);
WriteValue = write;
}
public override bool CanConvert(Type typeToConvert)
{
return stringToNumber.CanConvert(Nullable.GetUnderlyingType(typeToConvert) ?? typeToConvert);
}
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Null)
return null;
return stringToNumber.Read(ref reader, typeToConvert, options);
}
public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options)
{
if (!value.HasValue)
writer.WriteNullValue();
else
stringToNumber.Write(writer, value.Value, options);
}
}
然后是一个实用程序 class 来简化它们的使用。它包含不可概括的、类型精确的转换方法和设置:
static class StringToNumberUtil
{
public static readonly ISet<TypeCode> intCodes = new HashSet<TypeCode> { TypeCode.Byte, TypeCode.Int16, TypeCode.Int32 };
public static readonly ISet<TypeCode> longCodes = new HashSet<TypeCode> { TypeCode.Int64 };
public static readonly ISet<TypeCode> decimalCodes = new HashSet<TypeCode> { TypeCode.Decimal };
public static readonly ISet<TypeCode> doubleCodes = new HashSet<TypeCode> { TypeCode.Double };
public static int ParseInt(string s) => int.Parse(s, CultureInfo.InvariantCulture);
public static long ParseLong(string s) => long.Parse(s, CultureInfo.InvariantCulture);
public static decimal ParseDecimal(string s) => decimal.Parse(s, CultureInfo.InvariantCulture);
public static double ParseDouble(string s) => double.Parse(s, CultureInfo.InvariantCulture);
public static int ReadInt(ref Utf8JsonReader reader) => reader.GetInt32();
public static long ReadLong(ref Utf8JsonReader reader) => reader.GetInt64();
public static decimal ReadDecimal(ref Utf8JsonReader reader) => reader.GetDecimal();
public static double ReadDouble(ref Utf8JsonReader reader) => reader.GetDouble();
public static void WriteInt(Utf8JsonWriter writer, int value) => writer.WriteStringValue(value.ToString(CultureInfo.InvariantCulture));
public static void WriteLong(Utf8JsonWriter writer, long value) => writer.WriteStringValue(value.ToString(CultureInfo.InvariantCulture));
public static void WriteDecimal(Utf8JsonWriter writer, decimal value) => writer.WriteStringValue(value.ToString(CultureInfo.InvariantCulture));
public static void WriteDouble(Utf8JsonWriter writer, double value) => writer.WriteStringValue(value.ToString(CultureInfo.InvariantCulture));
}
最后,您可以为各个数字类型定义方便 classes...
public class StringToIntConverter : StringToNumberConverter<int>
{
public StringToIntConverter()
: base(StringToNumberUtil.intCodes, StringToNumberUtil.ParseInt, StringToNumberUtil.ReadInt, StringToNumberUtil.WriteInt)
{
}
}
public class StringToNullableIntConverter : StringToNullableNumberConverter<int>
{
public StringToNullableIntConverter()
: base(StringToNumberUtil.intCodes, StringToNumberUtil.ParseInt, StringToNumberUtil.ReadInt, StringToNumberUtil.WriteInt)
{
}
}
... 并像这样在 JsonSerializerOptions 中注册它们:
var options = new JsonSerializerOptions {
...
};
options.Converters.Add(new StringToIntConverter());
options.Converters.Add(new StringToNullableIntConverter());
...
(如果您愿意,也可以直接注册转换器。)
options.Converters.Add(new StringToNumberConverter<int>(StringToNumberUtil.intCodes, StringToNumberUtil.ParseInt, StringToNumberUtil.ReadInt, StringToNumberUtil.WriteInt));
options.Converters.Add(new StringToNullableNumberConverter<int>(StringToNumberUtil.intCodes, StringToNumberUtil.ParseInt, StringToNumberUtil.ReadInt, StringToNumberUtil.WriteInt));
应反序列化为枚举的数字
如果您的 JSON 包含字符串编码的数字属性,您可以添加它,其值具有可表示为枚举的预定义含义。
public class StringToIntEnumConverter<T> : JsonConverter<T> where T : struct, System.Enum
{
private StringToIntConverter stringToInt = new StringToIntConverter();
public override bool CanConvert(Type typeToConvert)
{
return typeToConvert == typeof(T);
}
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
int val = stringToInt.Read(ref reader, typeToConvert, options);
string underlyingValue = val.ToString(CultureInfo.InvariantCulture);
return (T)Enum.Parse(typeof(T), underlyingValue);
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
var number = Convert.ChangeType(value, Enum.GetUnderlyingType(typeof(T)), CultureInfo.InvariantCulture);
writer.WriteStringValue(number.ToString());
}
}
public class StringToNullableIntEnumConverter<T> : JsonConverter<T?> where T : struct, System.Enum
{
private StringToIntEnumConverter<T> stringToIntEnum = new StringToIntEnumConverter<T>();
public override bool CanConvert(Type typeToConvert)
{
return stringToIntEnum.CanConvert(Nullable.GetUnderlyingType(typeToConvert) ?? typeToConvert);
}
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Null)
return null;
return stringToIntEnum.Read(ref reader, typeToConvert, options);
}
public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options)
{
if (!value.HasValue)
{
writer.WriteNullValue();
return;
}
stringToIntEnum.Write(writer, value.Value, options);
}
}
JsonSerializerOptions 中的用法:
var options = new JsonSerializerOptions {
...
};
options.Converters.Add(new StringToIntEnumConverter<OrderFlags>());
options.Converters.Add(new StringToNullableIntEnumConverter<OrderFlags>());
...