Asp .Net Core 系列:Asp .Net Core 配置 System.Text.Json

【C#】System.Text.Json `System.Text.Json` 是 .NET Core 3.0 及更高版本中引入的一个高性能的、低内存的 JSON 序列化与反序列化库。它是 .NET Core 和 .NET 5/6/7 等新版本中推荐的 JSON 处理方式,因为它比旧的 `Newtonsoft.Json`(通常称为 Json.NET)库在性能上有所提升,并且它是 Microsoft 官方维护的。 阅读详情

简介

System.Text.Json 命名空间提供用于序列化和反序列化 JavaScript 对象表示法 (JSON) 的功能。 序列化是将对象状态(即其属性的值)转换为可存储或传输的形式的过程。 序列化形式不包含有关对象的关联方法的任何信息。 反序列化从序列化形式重新构造对象。

System.Text.Json 库的设计强调对广泛的功能集实现高性能和低内存分配。 内置的 UTF-8 支持可优化读写以 UTF-8 编码的 JSON 文本的过程,UTF-8 编码是针对 Web 上的数据和磁盘上的文件的最普遍的编码方式。

库还提供了用于处理内存中文档对象模型 (DOM) 的类。 此功能允许对 JSON 文件或字符串中的元素进行随机访问。

该库是作为 .NET Core 3.0 及更高版本共享框架的一部分内置的。 源生成功能内置在 .NET 6 和更高版本的共享框架中。

对于早于 .NET Core 3.0 的框架版本,请安装 System.Text.Json NuGet 包。 包支持以下框架:

  • .NET Standard 2.0 及更高版本
  • .NET Framework 4.6.2 及更高版本
  • .NET Core 2.1 及更高版本
  • .NET 5 及更高版本

https://learn.microsoft.com/zh-cn/dotnet/standard/serialization/system-text-json/overview

Asp .Net Core 如何配置 System.Text.Json

所有配置

https://learn.microsoft.com/zh-cn/dotnet/api/system.text.json.jsonserializeroptions?view=net-8.0&source=recommendations

AllowTrailingCommas获取或设置一个值,该值指示要反序列化的 JSON 有效负载中是否允许(和忽略)对象或数组中 JSON 值的列表末尾多余的逗号。
Converters获取已注册的用户定义的转换器的列表。
Default获取使用默认配置的 的 JsonSerializerOptions 只读单一实例。
DefaultBufferSize获取或设置创建临时缓冲区时要使用的默认缓冲区大小(以字节为单位)。
DefaultIgnoreCondition获取或设置一个值,该值确定在序列化或反序列化期间何时忽略具有默认值的属性。 默认值为 Never。
DictionaryKeyPolicy获取或设置用于将 IDictionary 密钥名称转换为其他格式(如 camel 大小写)的策略。
Encoder获取或设置要在转义字符串时使用的编码器,或为 null(要使用默认编码器的话)。
IgnoreNullValues**已过时。**获取或设置一个值,该值指示在序列化和反序列化期间是否 null 忽略值。 默认值为 false。
IgnoreReadOnlyFields获取或设置一个值,该值指示在序列化期间是否忽略只读字段。 如果某字段用 readonly 关键字进行标记,则该字段为只读字段。 默认值为 false。
IgnoreReadOnlyProperties获取一个值,该值指示在序列化期间是否忽略只读属性。 默认值为 false。
IncludeFields获取或设置一个值,该值指示是否在序列化和反序列化期间处理字段。 默认值为 false。
IsReadOnly获取一个值,该值指示当前实例是否已锁定以供用户修改。
MaxDepth获取或设置序列化或反序列化 JSON 时允许的最大深度,默认值 0 表示最大深度为 64。
NumberHandling获取或设置一个 对象,该对象指定序列化或反序列化时应如何处理数字类型。
PreferredObjectCreationHandling获取或设置反序列化 JSON 时属性的首选对象创建处理。
PropertyNameCaseInsensitive获取或设置一个值,该值指示属性名称在反序列化期间是否使用不区分大小写的比较。 默认值为 false。
PropertyNamingPolicy获取或设置一个值,该值指定用于将对象的属性名称转换为其他格式(例如 camel 大小写)的策略;若为 null,则保持属性名称不变。
ReadCommentHandling获取或设置一个值,该值定义反序列化过程中如何处理注释。
ReferenceHandler获取或设置一个 对象,该对象指定在读取和写入 JSON 时如何处理对象引用。
TypeInfoResolver获取或设置 JsonTypeInfo 此实例使用的协定解析程序。
TypeInfoResolverChain获取此实例使用的已 JsonTypeInfo 链接协定解析程序的列表。
UnknownTypeHandling获取或设置一个 对象,该对象指定如何在反序列化过程中反序列化声明为 Object 的类型。
UnmappedMemberHandling获取或设置一个 对象,该对象指定在反序列化对象类型时如何处理 JsonSerializer 无法映射到特定 .NET 成员的 JSON 属性。
WriteIndented获取或设置一个值,该值指示 JSON 是否应使用美观打印。 默认情况下,不使用任何额外的空白来序列化 JSON。

全局配置

在 AddControllers() 后面添加 AddJsonOptions 方法

   builder.Services.AddControllers().AddJsonOptions(options=> {
       options.JsonSerializerOptions.PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase;
   });

对比 Newtonsoft.Json

https://learn.microsoft.com/zh-cn/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-8-0#table-of-differences

Newtonsoft.Json 功能System.Text.Json 等效
默认情况下不区分大小写的反序列化✔️ PropertyNameCaseInsensitive 全局设置
Camel 大小写属性名称✔️ PropertyNamingPolicy 全局设置
对属性名称采用蛇形命名法✔️ 蛇形命名法命名策略
最小字符转义✔️ 严格字符转义,可配置
NullValueHandling.Ignore 全局设置✔️ DefaultIgnoreCondition 全局选项
允许注释✔️ ReadCommentHandling 全局设置
允许尾随逗号✔️ AllowTrailingCommas 全局设置
自定义转换器注册✔️ 优先级顺序不同
默认情况下无最大深度✔️ 默认最大深度为 64,可配置
PreserveReferencesHandling 全局设置✔️ ReferenceHandling 全局设置
序列化或反序列化带引号的数字✔️ [NumberHandling 全局设置,JsonNumberHandling] 特性
反序列化为不可变类和结构✔️ JsonConstructor,C# 9 记录
支持字段✔️ [IncludeFields 全局设置,JsonInclude] 特性
DefaultValueHandling 全局设置✔️ DefaultIgnoreCondition 全局设置
[JsonProperty] 上的 NullValueHandling 设置✔️ JsonIgnore 特性
[JsonProperty] 上的 DefaultValueHandling 设置✔️ JsonIgnore 特性
反序列化具有非字符串键的 Dictionary✔️ 受支持
支持非公共属性资源库和 Getter✔️ JsonInclude 特性
[JsonConstructor] 特性✔️ [JsonConstructor] 特性
ReferenceLoopHandling 全局设置✔️ ReferenceHandling 全局设置
回调✔️ 回调
NaN、Infinity、-Infinity✔️ 受支持
[JsonProperty] 特性上的 Required 设置✔️ [JsonRequired] 特性和 C# 必需的修饰符
DefaultContractResolver 用于忽略属性✔️ DefaultJsonTypeInfoResolver 类
多态序列化✔️ [JsonDerivedType] 特性
多态反序列化✔️ [JsonDerivedType] 特性上的类型鉴别器
反序列化字符串枚举值✔️ 反序列化字符串枚举值
MissingMemberHandling 全局设置✔️ 处理缺少的成员
在没有资源库的情况下填充属性✔️ 在没有资源库的情况下填充属性
ObjectCreationHandling 全局设置✔️ 重用而不是替换属性
支持范围广泛的类型⚠️ ⚠
将推断类型反序列化为 object 属性⚠️ ⚠
将 JSON null 文本反序列化为不可为 null 的值类型⚠️ ⚠
DateTimeZoneHandling、DateFormatString 设置⚠️ ⚠
JsonConvert.PopulateObject 方法⚠️ ⚠
支持 System.Runtime.Serialization 特性⚠️ ⚠
JsonObjectAttribute⚠️ ⚠
允许不带引号的属性名称❌设计上不受支持
字符串值前后允许单引号❌设计上不受支持
对字符串属性允许非字符串 JSON 值❌设计上不受支持
TypeNameHandling.All 全局设置❌设计上不受支持
支持 JsonPath 查询❌不支持
可配置的限制❌不支持

无实体类型下操作 Json

类似 Newtonsoft.Json,在没有实体类的情况下,也可以使用 JsonNode/JsonValue/JsonArray/JsonObject 操作 json。

image

自定义转换器

  • DateTimeConverter - DateTime 类型转换器
  • DateOnlyConverter - DateOnly 类型转换器
  • TimeOnlyConverter - TimeOnly 类型转换器
  • LongConverter - Long 类型转换器
  • Int32Converter - Int 类型转换器
  • DecimalConverter - Decimal 类型转换器
  • StringConverter - String 类型转换器
  • BooleanConverter - Boolean 类型转换器
  • NullAbleConverter - 可空类型转换器
  • EnumConverter - 枚举类型转换器
 public class JsonConverterExtensions
 {
     /// <summary>
     /// DateTime类型转换器
     /// </summary>
     public sealed class DateTimeConverter : JsonConverter<DateTime>
     {
         /// <summary>
         /// 格式化
         /// </summary>
         public string Format { get; set; } = "yyyy-MM-dd HH:mm:ss";
         /// <summary>
         /// 使用默认格式,同: <c>System.Text.Json.JsonSerializer.Serialize(obj)</c>
         /// </summary>
         public DateTimeConverter() { }
         /// <summary>
         /// 指定格式化字符串
         /// </summary>
         public DateTimeConverter(string format)
         {
             Format = format;
         }

         public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
         {
             if (reader.TokenType == JsonTokenType.Null)
                 return default;

             if (string.IsNullOrEmpty(reader.GetString()))
             {
                 return default;
             }
             else
             {
                 return DateTime.TryParse(reader.GetString(), out DateTime result) ? result : default;
             }
         }

         public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
         {
             writer.WriteStringValue(value.ToString(Format, CultureInfo.InvariantCulture));
         }
     }

     /// <summary>
     /// DateOnly类型转换器
     /// </summary>
     public sealed class DateOnlyConverter : JsonConverter<DateOnly>
     {
         /// <summary>
         /// 格式化
         /// </summary>
         public string Format { get; set; } = "yyyy-MM-dd";
         /// <summary>
         /// 使用默认格式,同: <c>System.Text.Json.JsonSerializer.Serialize(obj)</c>
         /// </summary>
         public DateOnlyConverter() { }
         /// <summary>
         /// 指定格式化字符串
         /// </summary>
         public DateOnlyConverter(string format)
         {
             Format = format;
         }

         public override DateOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
         {
             if (reader.TokenType == JsonTokenType.Null)
                 return default;

             if (string.IsNullOrEmpty(reader.GetString()))
             {
                 return default;
             }
             else
             {
                 return DateOnly.TryParse(reader.GetString(), out DateOnly result) ? result : default;
             }
         }

         public override void Write(Utf8JsonWriter writer, DateOnly value, JsonSerializerOptions options)
         {
             writer.WriteStringValue(value.ToString(Format, CultureInfo.InvariantCulture));
         }
     }

     /// <summary>
     /// TimeOnly类型转换器
     /// </summary>
     public sealed class TimeOnlyConverter : JsonConverter<TimeOnly>
     {
         /// <summary>
         /// 格式化
         /// </summary>
         public string Format { get; set; } = "HH:mm:ss";
         /// <summary>
         /// 使用默认格式,同: <c>System.Text.Json.JsonSerializer.Serialize(obj)</c>
         /// </summary>
         public TimeOnlyConverter() { }
         /// <summary>
         /// 指定格式化字符串
         /// </summary>
         public TimeOnlyConverter(string format)
         {
             Format = format;
         }

         public override TimeOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
         {
             if (reader.TokenType == JsonTokenType.Null)
                 return default;

             if (string.IsNullOrEmpty(reader.GetString()))
             {
                 return default;
             }
             else
             {
                 return TimeOnly.TryParse(reader.GetString(), out TimeOnly result) ? result : default;
             }
         }

         public override void Write(Utf8JsonWriter writer, TimeOnly value, JsonSerializerOptions options)
         {
             writer.WriteStringValue(value.ToString(Format, CultureInfo.InvariantCulture));
         }
     }

     /// <summary>
     /// Long类型转换器
     /// </summary>
     public sealed class LongConverter : JsonConverter<long>
     {
         public override long Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options)
         {
             if (reader.TokenType == JsonTokenType.Null)
                 return default;

             if (reader.TokenType == JsonTokenType.String)
             {
                 ReadOnlySpan<byte> span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
                 if (Utf8Parser.TryParse(span, out long number, out int bytesConsumed) && span.Length == bytesConsumed)
                     return number;

                 if (long.TryParse(reader.GetString(), out number))
                     return number;
             }

             return reader.GetInt64();
         }


         /// <summary>
         /// 注意这里在写入的时候转成了字符串
         /// </summary>
         /// <param name="writer"></param>
         /// <param name="value"></param>
         /// <param name="options"></param>
         public override void Write(Utf8JsonWriter writer, long value, JsonSerializerOptions options)
         {
             writer.WriteStringValue(Convert.ToString(value));
         }
     }

     /// <summary>
     /// Int类型转换器
     /// </summary>
     public sealed class Int32Converter : JsonConverter<int>
     {
         public override int Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options)
         {
             if (reader.TokenType == JsonTokenType.Null)
                 return default;

             if (reader.TokenType == JsonTokenType.String)
             {
                 ReadOnlySpan<byte> span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
                 if (Utf8Parser.TryParse(span, out int number, out int bytesConsumed) && span.Length == bytesConsumed)
                     return number;

                 if (int.TryParse(reader.GetString(), out number))
                     return number;
             }

             return reader.GetInt32();
         }

         public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options)
         {
             writer.WriteNumberValue(value);
         }
     }

     /// <summary>
     /// Decimal类型转换器
     /// </summary>
     public sealed class DecimalConverter : JsonConverter<decimal>
     {
         public override decimal Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options)
         {
             if (reader.TokenType == JsonTokenType.String)
             {
                 ReadOnlySpan<byte> span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
                 if (Utf8Parser.TryParse(span, out decimal number, out int bytesConsumed) && span.Length == bytesConsumed)
                     return number;

                 if (decimal.TryParse(reader.GetString(), out number))
                     return number;
             }

             if (reader.TokenType == JsonTokenType.Null)
                 return default;

             return reader.GetDecimal();
         }

         public override void Write(Utf8JsonWriter writer, decimal value, JsonSerializerOptions options)
         {
             writer.WriteNumberValue(value);
         }
     }

     /// <summary>
     /// String类型转换器
     /// </summary>
     public sealed class StringConverter : JsonConverter<string>
     {
         public override string Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options)
         {
             switch (reader.TokenType)
             {
                 case JsonTokenType.None:
                 case JsonTokenType.Null:
                     return null;
                 case JsonTokenType.Number:
                     return reader.GetDouble().ToString();
                 case JsonTokenType.True:
                     return "true";
                 case JsonTokenType.False:
                     return "false";
                 default:
                     return reader.GetString();
             }
             return reader.GetString();
         }

         public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
         {
             writer.WriteStringValue(value);
         }
     }

     /// <summary>
     /// Boolean类型转换器
     /// </summary>
     public sealed class BooleanConverter : JsonConverter<bool>
     {
         public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
         {
             switch (reader.TokenType)
             {
                 case JsonTokenType.None:
                 case JsonTokenType.Null:
                     throw new Exception($"无法将 null 反序列化为 bool!");
                 case JsonTokenType.Number:
                     var d = reader.GetDouble();
                     return !(d == 0);
                 case JsonTokenType.String:
                     var str = reader.GetString();
                     if (string.Equals(str, "true", StringComparison.OrdinalIgnoreCase)) return true;
                     else if (string.Equals(str, "false", StringComparison.OrdinalIgnoreCase)) return false;
                     else throw new Exception($"无法将非 \"true\"或\"false\" 的字符串 转为 bool!");
                 case JsonTokenType.True: return true;
                 case JsonTokenType.False: return false;
                 default: throw new Exception($"无法将 {reader.TokenType} 反序列化为 bool!");
             }
         }

         public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options)
         {
             writer.WriteBooleanValue(value);
         }
     }

     /// <summary>
     /// 可空类型转换器
     /// </summary>
     public sealed class NullAbleConverter : JsonConverterFactory
     {
         /// <summary>
         /// 是否是Nullable类型的
         /// </summary>
         /// <param name="type"></param>
         private static bool IsNullable(Type type)
         {
             if (type == null) return false;
             return type.Name == "Nullable`1";
         }

         private static ConcurrentDictionary<Type, JsonConverter> _cache = new ConcurrentDictionary<Type, JsonConverter>();
         public override bool CanConvert(Type typeToConvert)
         {
             return IsNullable(typeToConvert);
         }

         public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
         {
             return _cache.GetOrAdd(typeToConvert, (c) =>
             {
                 return (JsonConverter)Activator.CreateInstance(typeof(NullAbleConverter<>).MakeGenericType(typeToConvert));
             });
         }
     }


     /// <summary>
     /// 泛型可空类型转换器
     /// </summary>
     /// <typeparam name="T"></typeparam>
     public sealed class NullAbleConverter<T> : JsonConverter<T>
     {

         /// <summary>
         /// 是否是Nullable类型的
         /// </summary>
         /// <param name="type"></param>
         private static bool IsNullable(Type type)
         {
             if (type == null) return false;
             return type.Name == "Nullable`1";
         }

         public NullAbleConverter()
         {
             s_UnderlyingType = Nullable.GetUnderlyingType(typeof(T));
         }
         private Type s_UnderlyingType = null;
         public override bool CanConvert(Type typeToConvert)
         {
             return IsNullable(typeToConvert);
         }

         public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
         {
             switch (reader.TokenType)
             {
                 case JsonTokenType.Null:
                 case JsonTokenType.None:
                     return default;
                 case JsonTokenType.String:
                     var str = reader.GetString();
                     if (str == string.Empty) return default;
                     return (T)JsonSerializer.Deserialize(ref reader, s_UnderlyingType, options);
                 default:
                     return (T)JsonSerializer.Deserialize(ref reader, s_UnderlyingType, options);
             }
         }

         public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
         {
             if (value == null)
             {
                 writer.WriteStringValue("null");
             }
             else
             {
                 JsonSerializer.Serialize(writer, value, value.GetType(), options);
             }
         }
     }

     /// <summary>
     /// 枚举类型转换器
     /// </summary>
     public sealed class EnumConverter : JsonConverterFactory
     {
         public EnumConverter(bool enum2String = true)
         {
             this.enum2String = enum2String;
         }
         private static ConcurrentDictionary<Type, JsonConverter> _cache2String = new ConcurrentDictionary<Type, JsonConverter>();
         private static ConcurrentDictionary<Type, JsonConverter> _cache2Numer = new ConcurrentDictionary<Type, JsonConverter>();
         private readonly bool enum2String;

         public sealed override bool CanConvert(Type typeToConvert)
         {
             return typeToConvert.IsEnum;
         }

         public sealed override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
         {
             var _cache = enum2String ? _cache2String : _cache2Numer;
             return _cache.GetOrAdd(typeToConvert, (c) =>
             {
                 var ctor = typeof(EnumConverter<>).MakeGenericType(typeToConvert).GetConstructor(new Type[] { typeof(bool) });
                 return (JsonConverter)ctor.Invoke(new object[] { enum2String });
             });
         }
     }

     /// <summary>
     /// 泛型枚举类型转换器
     /// </summary>
     /// <typeparam name="T"></typeparam>
     public sealed class EnumConverter<T> : JsonConverter<T> where T : struct, Enum
     {
         public EnumConverter(bool enum2String)
         {
             this.enum2String = enum2String;
         }
         private static readonly TypeCode s_enumTypeCode = Type.GetTypeCode(typeof(T));
         private readonly bool enum2String;

         public override bool CanConvert(Type type)
         {
             return type.IsEnum;
         }

         public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
         {
             switch (reader.TokenType)
             {
                 case JsonTokenType.String:
                     var str = reader.GetString();
                     return (T)Enum.Parse(typeof(T), str, true);
                 case JsonTokenType.Number:
                     switch (s_enumTypeCode)
                     {
                         case TypeCode.Int32:
                             {
                                 if (reader.TryGetInt32(out var value8))
                                 {
                                     return Unsafe.As<int, T>(ref value8);
                                 }
                                 break;
                             }
                         case TypeCode.UInt32:
                             {
                                 if (reader.TryGetUInt32(out var value4))
                                 {
                                     return Unsafe.As<uint, T>(ref value4);
                                 }
                                 break;
                             }
                         case TypeCode.UInt64:
                             {
                                 if (reader.TryGetUInt64(out var value6))
                                 {
                                     return Unsafe.As<ulong, T>(ref value6);
                                 }
                                 break;
                             }
                         case TypeCode.Int64:
                             {
                                 if (reader.TryGetInt64(out var value2))
                                 {
                                     return Unsafe.As<long, T>(ref value2);
                                 }
                                 break;
                             }
                         case TypeCode.SByte:
                             {
                                 if (reader.TryGetSByte(out var value7))
                                 {
                                     return Unsafe.As<sbyte, T>(ref value7);
                                 }
                                 break;
                             }
                         case TypeCode.Byte:
                             {
                                 if (reader.TryGetByte(out var value5))
                                 {
                                     return Unsafe.As<byte, T>(ref value5);
                                 }
                                 break;
                             }
                         case TypeCode.Int16:
                             {
                                 if (reader.TryGetInt16(out var value3))
                                 {
                                     return Unsafe.As<short, T>(ref value3);
                                 }
                                 break;
                             }
                         case TypeCode.UInt16:
                             {
                                 if (reader.TryGetUInt16(out var value))
                                 {
                                     return Unsafe.As<ushort, T>(ref value);
                                 }
                                 break;
                             }
                     }
                     throw new Exception($"无法从 {JsonTokenType.Number} 转为枚举!");
                 default:
                     throw new Exception($"无法从 {reader.TokenType} 转为枚举!");
             }
         }

         public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
         {
             if (enum2String)
             {
                 writer.WriteStringValue(value.ToString());
             }
             else
             {
                 switch (s_enumTypeCode)
                 {
                     case TypeCode.Int32:
                         writer.WriteNumberValue(Unsafe.As<T, int>(ref value));
                         break;
                     case TypeCode.UInt32:
                         writer.WriteNumberValue(Unsafe.As<T, uint>(ref value));
                         break;
                     case TypeCode.UInt64:
                         writer.WriteNumberValue(Unsafe.As<T, ulong>(ref value));
                         break;
                     case TypeCode.Int64:
                         writer.WriteNumberValue(Unsafe.As<T, long>(ref value));
                         break;
                     case TypeCode.Int16:
                         writer.WriteNumberValue(Unsafe.As<T, short>(ref value));
                         break;
                     case TypeCode.UInt16:
                         writer.WriteNumberValue(Unsafe.As<T, ushort>(ref value));
                         break;
                     case TypeCode.Byte:
                         writer.WriteNumberValue(Unsafe.As<T, byte>(ref value));
                         break;
                     case TypeCode.SByte:
                         writer.WriteNumberValue(Unsafe.As<T, sbyte>(ref value));
                         break;
                     default:
                         throw new Exception($"无法将 {s_enumTypeCode} 序列化!");
                 }
             }
         }
     }
 }

处理 Dynamic 类型

    /// <summary>
    /// JsonElement转Dynamic类型
    /// </summary>
    public class JsonDynamicAccessor : DynamicObject
    {
        private readonly JsonElement _content;
        public JsonDynamicAccessor(JsonElement content)
        {
            _content = content;
        }

        public override bool TryGetMember(GetMemberBinder binder, out dynamic result)
        {
            if (_content.TryGetProperty(binder.Name, out JsonElement value))
            {
                result = Obtain(value);
                return true;
            }
            else
            {
                result = null;
                return false;
            }
        }

        private dynamic Obtain(in JsonElement element)
        {
            switch (element.ValueKind)
            {
                case JsonValueKind.String: return element.GetString();
                case JsonValueKind.Null: return null;
                case JsonValueKind.False: return false;
                case JsonValueKind.True: return true;
                case JsonValueKind.Number:
                    if (element.TryGetInt64(out long longNumber))
                    {
                        return longNumber;
                    }
                    if (element.TryGetInt32(out int intNumber))
                    {
                        return intNumber;
                    }
                    if (element.TryGetDecimal(out decimal decimalNumber))
                    {
                        return decimalNumber;
                    }
                    if (element.TryGetDouble(out double doubleNumber))
                    {
                        return doubleNumber;
                    }
                    if (element.TryGetInt16(out short shortNumber))
                    {
                        return shortNumber;
                    }
                    break;
                default: break;
            }

            if (element.ValueKind == JsonValueKind.Array)
            {
                return element.EnumerateArray().Select(item => Obtain(item)).ToList();
            }
            else
            {
                return new JsonDynamicAccessor(element);
            }
        }
    }

封装常用配置

    /// <summary>
    /// Json序列化扩展
    /// </summary>
    public static class JsonSerializeExtensions
    {
        public static IMvcBuilder AddMCodeJsonOptions(this IMvcBuilder builder,Action<JsonOptions> configure = null)
        {
            builder.AddJsonOptions(options =>
             {
                 // 设置要在转义字符串时使用的编码器,或为 null(要使用默认编码器的话)
                 options.JsonSerializerOptions.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; // 非ascii码转换
                 // 是否在序列化和反序列化期间处理字段,默认值为 false
                 options.JsonSerializerOptions.IncludeFields = true;
                 // 在序列化期间是否忽略只读字段.如果某字段用 readonly 关键字进行标记,则该字段为只读字段。 默认值为 false
                 options.JsonSerializerOptions.IgnoreReadOnlyFields = false;
                 // 在序列化或反序列化期间何时忽略具有默认值的属性,默认值:Never属性总是被序列化和反序列化,与IgnoreNullValues配置无关。
                 // WhenWritingNull:(如果其值为空,则忽略属性。这只应用于引用类型的属性和字段)
                 options.JsonSerializerOptions.DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.Never;
                 // 指示属性名称在反序列化期间是否使用不区分大小写的比较,默认值为 false
                 options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
                 // 指定用于将对象的属性名称转换为其他格式(例如 camel 大小写)的策略;若为 null,则保持属性名称不变。
                 options.JsonSerializerOptions.PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase;
                 // 反序列化过程中如何处理注释
                 options.JsonSerializerOptions.ReadCommentHandling = System.Text.Json.JsonCommentHandling.Skip;
                 // 该值指示要反序列化的 JSON 有效负载中是否允许(和忽略)对象或数组中 JSON 值的列表末尾多余的逗号。
                 options.JsonSerializerOptions.AllowTrailingCommas = true;
                 //允许 {"Prop":"NaN"} 浮点型,但不允许 {\"Prop\":NaN},这点无法做到与Newtonsoft兼容
                 options.JsonSerializerOptions.NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.AllowNamedFloatingPointLiterals;
                 // 是否格式化文本
                 options.JsonSerializerOptions.WriteIndented = true;
                 // 处理循环引用类型
                 options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;

                 options.JsonSerializerOptions.Converters.Add(new JsonConverterExtensions.DateTimeConverter());
                 options.JsonSerializerOptions.Converters.Add(new JsonConverterExtensions.DateOnlyConverter());
                 options.JsonSerializerOptions.Converters.Add(new JsonConverterExtensions.TimeOnlyConverter());
                 options.JsonSerializerOptions.Converters.Add(new JsonConverterExtensions.LongConverter());
                 options.JsonSerializerOptions.Converters.Add(new JsonConverterExtensions.Int32Converter());
                 options.JsonSerializerOptions.Converters.Add(new JsonConverterExtensions.DecimalConverter());
                 options.JsonSerializerOptions.Converters.Add(new JsonConverterExtensions.BooleanConverter()); ;
                 options.JsonSerializerOptions.Converters.Add(new JsonConverterExtensions.StringConverter());
                 options.JsonSerializerOptions.Converters.Add(new JsonConverterExtensions.EnumConverter());
                 options.JsonSerializerOptions.Converters.Add(new JsonConverterExtensions.NullAbleConverter());

                 configure.Invoke(options);
             });



            return builder;
        }
    }



    builder.Services.AddControllers().AddMCodeJsonOptions();

封装 JsonHelper 帮助类

    /// <summary>
    /// Json序列化反序列化类
    /// </summary>
    public class JsonHelper
    {
        public static JsonSerializerOptions DefaultSerializerOptions
        {
            get
            {
                var options = new JsonSerializerOptions();

                // 设置要在转义字符串时使用的编码器,或为 null(要使用默认编码器的话)
                options.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping;
                // 是否在序列化和反序列化期间处理字段,默认值为 false
                options.IncludeFields = true;
                // 在序列化期间是否忽略只读字段.如果某字段用 readonly 关键字进行标记,则该字段为只读字段。 默认值为 false
                options.IgnoreReadOnlyFields = false;
                // 在序列化或反序列化期间何时忽略具有默认值的属性,默认值:Never属性总是被序列化和反序列化,与IgnoreNullValues配置无关。
                // WhenWritingNull:(如果其值为空,则忽略属性。这只应用于引用类型的属性和字段)
                options.DefaultIgnoreCondition = JsonIgnoreCondition.Never;
                // 指示属性名称在反序列化期间是否使用不区分大小写的比较,默认值为 false
                options.PropertyNameCaseInsensitive = true;
                // 指定用于将对象的属性名称转换为其他格式(例如 camel 大小写)的策略;若为 null,则保持属性名称不变。
                options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
                // 反序列化过程中如何处理注释
                options.ReadCommentHandling = JsonCommentHandling.Skip;
                // 该值指示要反序列化的 JSON 有效负载中是否允许(和忽略)对象或数组中 JSON 值的列表末尾多余的逗号。
                options.AllowTrailingCommas = true;
                //允许 {"Prop":"NaN"} 浮点型,但不允许 {\"Prop\":NaN},这点无法做到与Newtonsoft兼容
                options.NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.AllowNamedFloatingPointLiterals;
                // 是否格式化文本
                options.WriteIndented = true;
                // 处理循环引用类型
                options.ReferenceHandler = ReferenceHandler.IgnoreCycles;

                options.Converters.Add(new JsonConverterExtensions.DateTimeConverter());
                options.Converters.Add(new JsonConverterExtensions.DateOnlyConverter());
                options.Converters.Add(new JsonConverterExtensions.TimeOnlyConverter());
                options.Converters.Add(new JsonConverterExtensions.LongConverter());
                options.Converters.Add(new JsonConverterExtensions.Int32Converter());
                options.Converters.Add(new JsonConverterExtensions.DecimalConverter());
                options.Converters.Add(new JsonConverterExtensions.BooleanConverter()); ;
                options.Converters.Add(new JsonConverterExtensions.StringConverter());
                options.Converters.Add(new JsonConverterExtensions.EnumConverter());
                options.Converters.Add(new JsonConverterExtensions.NullAbleConverter());

                return options;
            }
        }

        public static T Deserialize<T>(string json, JsonSerializerOptions serializerOptions = null)
        {
            if (string.IsNullOrEmpty(json)) return default;

            if (serializerOptions == null) serializerOptions = DefaultSerializerOptions;

            //值类型和String类型
            if (typeof(T).IsValueType || typeof(T) == typeof(string))
            {
                return (T)Convert.ChangeType(json, typeof(T));
            }
            // dynamic类型
            if (typeof(T) == typeof(object))
            {
                return (dynamic)new JsonDynamicAccessor(JsonSerializer.Deserialize<JsonElement>(json));
            }

            return JsonSerializer.Deserialize<T>(json, serializerOptions);
        }

        public static string Serialize<T>(T obj, JsonSerializerOptions serializerOptions = null)
        {
            if (obj is null) return string.Empty;
            if (obj is string) return obj.ToString();
            if (serializerOptions == null) serializerOptions = DefaultSerializerOptions;
            return JsonSerializer.Serialize(obj, serializerOptions);
        }

JSON序列化-System.Text.Json JSON序列化,System.Text.Json 阅读详情

相关推荐

使用ASP.NET core 6.0的心得

ASP.NET Core 6.0

QW1256csdn的博客 873

c#:System.Text.Json 的使用一

System.Text.Json 的使用,与 Newtonsoft.Json 的对比

火焰 1万+

Asp .Net Core 集成 Newtonsoft.Json

Newtonsoft.Json是一个在.NET环境下开源的JSON格式序列化和反序列化的类库。它可以将.NET对象转换为JSON格式的字符串,也可以将JSON格式的字符串转换为.NET对象。这个类库在.NET开发中被广泛使用,因为它功能强大、易于使用,并且有良好的性能。序列化:将.NET对象转换为JSON字符串。这通常用于将数据发送到Web服务或保存到文件。反序列化:将JSON字符串转换为.NET对象。这通常用于从Web服务接收数据或从文件中读取数据。

softshow1026的博客 1892

ASP.NET Core 3.1系列(29)——System.Text.Json实现JSON的序列化和反序列化

在Web开发中,JSON数据可以说是无处不在。由于具有轻量、易读等优点,JSON已经成为当前主流的数据传输格式。在之前,大多数项目都会使用组件来实现JSON的序列化和反序列化操作,而从开始,微软提供的已经相当出色,其效率相比前者可以说是有过之而无不及,下面就来介绍一下它的使用方法。本文简单介绍了中关于JSON序列化和反序列化操作,主要通过来实现。如果你觉得微软提供的JSON序列化工具不好用,那也可以使用,我也会在下一篇博客中介绍关于的使用方法。

HerryDong的博客 3236

在AVP28335(兼容德州仪器TMS320-28335) 上实现ModbusSlaveRTU-485源码及注释。

今天先把题目先给出来, 在近两天会把源码 (含详细注释 )及部署、测试结果给出来, 希望能给大家帮助。(全免费 我有点缺德,断了别人的一个财路)。

FOOLCODE的专栏 642

DVWA靶场实战:手把手教你绕过文件上传限制,用蚁剑连接一句话木马

本文详细介绍了在DVWA靶场中如何绕过文件上传限制并利用蚁剑连接一句话木马的实战技巧。通过分析文件上传漏洞原理、创建一句话木马、使用Burp Suite拦截修改请求以及配置蚁剑连接WebShell的全过程,帮助安全研究人员深入理解Web安全防护与攻击技术。

weixin_30335575的博客 90

Asp .Net Core 系列:Asp .Net Core 集成 Newtonsoft.Json

Newtonsoft.Json是一个在.NET环境下开源的JSON格式序列化和反序列化的类库。它可以将.NET对象转换为JSON格式的字符串,也可以将JSON格式的字符串转换为.NET对象。这个类库在.NET开发中被广泛使用,因为它功能强大、易于使用,并且有良好的性能。序列化:将.NET对象转换为JSON字符串。这通常用于将数据发送到Web服务或保存到文件。反序列化:将JSON字符串转换为.NET对象。这通常用于从Web服务接收数据或从文件中读取数据。

程序人生 2415

别再到处找模板了!我用这套软著申请材料(含用户手册+源代码模板)两个月搞定

本文提供了一套高效的软著申请材料模板,包括用户手册和源代码模板,帮助开发者两个月内快速完成申请。通过实战指南和常见误区解析,详细介绍了材料准备的规范与技巧,确保格式合规性,大幅提升审核通过率。

weixin_29268637的博客 548

.NET Core 中 System.Text.Json 与 Newtonsoft.Json 深度对比:用法、性能与场景选型

在.NET Core 开发中,JSON 序列化与反序列化是高频操作,无论是 API 接口数据传输、配置文件解析还是缓存数据处理,都离不开 JSON 库的支持。目前.NET 生态中最主流的两款 JSON 处理库分别是微软官方内置的System.Text.Json和第三方经典库Newtonsoft.Json(Json.NET)。本文将从包特性、用法差异、性能表现、适用场景四个维度进行深度对比,并补充其他常用 JSON 库,帮助开发者做出更合适的技术选型。

二十多岁的你迷茫又着急,想要车子,想要房子,想要享受旅行,你那么浮躁,拿什么看透人生! 1778

人形机器人骨骼材料选型实战:从铝合金到碳纤维的5种方案对比

本文深入探讨人形机器人骨骼材料的选型策略,对比铝合金、镁合金、钛合金、碳纤维复合材料及3D打印材料等5种方案的性能优劣。通过建立强度-重量比、疲劳寿命等核心评价维度,结合具体应用场景,提供科学的材料决策方法论,助力开发者优化机器人结构设计,实现性能与成本的平衡。

weixin_29199873的博客 437

k8s部署 普罗米修斯(Prometheus)监控

Prometheus是一个开源的服务监控系统和时序数据库其提供了通用的数据模型和快捷数据采集、存储和查询接口它的核心组件Prometheus服务器定期从静态配置的监控目标或者基于服务发现自动配置的目标中进行拉 取数据新拉取到啊的 数据大于配置的内存缓存区时,数据就会持久化到存储设备当中。

D2961953033的博客 3854

ASP.NET Core 3.X后出现 [The JSON value could not be converted to System.Nullable] 错误

一、ASP.NET Core从2.1 --> 3.1后出现 [The JSON value could not be converted to System.Nullable]错误 解决方案如下: 出现如上的错误,是因为从ASP.NET Core 3.0开始,默认使用了微软内置的JSON序列化和反序列化类库:System.Text.Json,此类库中并没有像Json.NET中实现这些特殊数据类型的JSON转换,所以如果遇到这个问题: 你可以手动安装Json.NET这替换System.Text.Jso

weixin_44146294的博客 8423

OpenHarmony开发一个App,安装到BearPi-HM Micro开发板

建议先阅读我们这篇,这里面详细介绍了,以及hap包安装的全过程。本篇是介绍如何开发一个App,然后安装到开发板上,比如hap包,无法安装等问题。

Halifax 1030

ASP.NET Core Web API设置响应输出的Json数据格式的两种方式

在ASP.NET Core Web API中设置响应输出Json数据格式有两种方式,可以通过添加或JSON序列化和反序列化库在应用程序中全局设置接口响应的Json数据格式,本文示例使用的是新的Minimal API模式。

专注于C#/.NET/.NET Core学习、工作、面试干货和实战教程分享。这里聚焦了大量的C#/.NET/.NET Core优质文章、开源项目、实用工具和学习、工作、面试心得。 2195

OpenGL ES 3.0 从入门到精通系统性学习教程

因为在工作中频繁使用 OpenGL ES 做一些特效、滤镜之类的效果,加上平时学到的的知识点也比较细碎,就想着去系统地学习下 OpenGL ES 相关开发知识,并将学习过程记录下来。

字节流动 3万+

车辆网络安全ISO/SAE 21434解读(一)概述

ISO/SAE 21434 《道路车辆 网络安全工程》是SAE和ISO共同制定的一项针对道路车辆的网络安全标准,它是一个面向汽车行业全供应链(OEM及各级供应商)的车辆网络安全管理指导文件

xiaoxiabang的博客 8423

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

NSwag与System.Text.Json:.NET Core 3.0+的终极JSON序列化指南

在现代.NET开发中,NSwag与System.Text.Json的结合为API开发和客户端代码生成提供了强大的工具链。NSwag作为.NET生态中领先的Swagger/OpenAPI工具链,与System.Text.Json的高性能JSON序列化库完美集成,为开发者提供了从API设计到客户端代码生成的完整解决方案。本文将深入探讨如何利用NSwag和System.Text.Json构建高效、类型安

gitblog_00885的博客 911

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

.NET Core3发布Json API

我们给DNC3(.NET Core 3)上了一个新包,叫做System.Text.Json(点我下载),支持读写器,DOM(文档对象模型),和序列化,在这篇博文里,我会告...

dotNET跨平台 1564

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

[译]试用新的System.Text.Json API

译注 可能有的小伙伴已经知道了,在.NET Core 3.0中微软加入了对JSON的内置支持。 一直以来.NET开发者们已经习惯使用Json.NET这个强大的库来处理JSON。 那么.NET为什么要增加JSON的内置支持呢? 最近,.NET的官方博客再次发表文章说明了这么做的原因、并介绍了相关API的用法。 现翻译出来分享给大家。 原文地址: https://devblogs.micr...

weixin_30709929的博客 1559

C#.NET System.Text.Json 详解

System.Text.Json 是 .NET Core 3.0+ 和 .NET 5+ 平台自带的高性能 JSON 序列化/反序列化库,位于 System.Text.Json 命名空间,无需额外安装包(针对早期版本可安装 System.Text.Json) 设计目标:极致性能、低分配、零依赖,并且与现代 .NET 平台深度集成,支持 UTF-8 原生处理、管道化处理、源生成等高级功能。 相比 Newtonsoft.Json,更轻量、速度更快,但在灵活性和功能丰富度(如动态 LINQ to JSON)上略

tangPHP的博客 924

解决.NET 8开发痛点:System.Text.Json源生成器与ASP.NET Core控制器集成全指南

在.NET 8应用开发中,System.Text.Json源生成器与ASP.NET Core控制器的集成问题已成为影响开发效率的常见痛点。开发团队常遇到序列化性能瓶颈、类型映射冲突和编译时错误等问题,这些问题在高并发API场景下尤为突出。官方文档[docs/coding-guidelines/source-generator-guidelines.md](https://link.gitcode....

gitblog_00947的博客 519

ASP.NET Core Json格式化响应数据

设置 ASP.NET Core Web API 中响应数据的格式添加 XML 格式支持 配置基于 System.Text.Json 的格式化程序 添加基于 Newtonsoft.Json 的 JSON 格式支持 ASP.NET Core 中的模型绑定 *1、注意在 ASP.NET Core 3.0 中MVC与 API 是不一样的基于 System.Text.Json 格式MVC:se...

KingCruel的专栏 1052

无法获取未定义或 null 引用的属性“text”_在.Net Core 3.0中尝试新的System.Text.Json API...

.NET Core 3.0提供了一个名为System.Text.Json的全新命名空间,它支持reader/writer,文档对象模型(DOM)和序列化程序。在此博客文章中,我将介绍它如何工作以及如何使用。官方文档获取JSON库如果以.NET Core为目标,请安装.NET Core 3.0及以上版本,该版本提供了新的JSON库和ASP.NET Core集成。如果以.NET Standa...

weixin_39748838的博客 1581

asp.net core 3.0 MVC JSON 全局配置

asp.net core 3.0 MVC JSON 全局配置 System.Text.Json(default) startup配置代码如下: using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Unicode; public void ConfigureServices(IService...

深蓝旭的博客 1316
上一篇: Nginx系列:简介和安装(基于yum和源码的方式)
下一篇: MyBatis 源码系列:MyBatis 解析配置文件、二级缓存、SQL
Code技术分享
博客等级 码龄7年 3327粉丝 87原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值