seo推广案例,seo职位具体做什么,网站字体一般大小,常州网站建设代理商作者#xff1a;Eirik Tsarpalis - 首席软件工程师 排版#xff1a;Alan Wang System.Text.Json 的9.0 版本包含许多功能#xff0c;主要侧重于 JSON 架构和智能应用程序支持。它还包括一些备受期待的增强功能#xff0c;例如可空引用类型支持、自定义枚举成员名称、无序元… 作者Eirik Tsarpalis - 首席软件工程师 排版Alan Wang System.Text.Json 的9.0 版本包含许多功能主要侧重于 JSON 架构和智能应用程序支持。它还包括一些备受期待的增强功能例如可空引用类型支持、自定义枚举成员名称、无序元数据反序列化和自定义序列化缩进。
获取最新信息
您可以通过引用 System.Text.Json NuGet 包的最新版本或 .NET 9 的最新 SDK来尝试新功能。
JSON 架构导出器
新的 JsonSchemaExporter 类可以使用 JsonSerializerOptions 或 JsonTypeInfo 实例从 .NET 类型中提取 JSON 架构文档
using System.Text.Json.Schema;
JsonSerializerOptions options JsonSerializerOptions.Default;
JsonNode schema options.GetJsonSchemaAsNode(typeof(Person));
Console.WriteLine(schema.ToString());
//{
// type: [object, null],
// properties: {
// Name: { type: string },
// Age: { type: integer },
// Address: { type: [string, null], default: null }
// },
// required: [Name, Age]
//}
record Person(string Name, int Age, string? Address null);生成的模式为该类型提供了 JSON 序列化契约的规范。从这个例子中可以看出它区分了可空属性和不可空属性并根据构造函数参数是否可选来填充“required”关键字。模式的输出可以通过在 JsonSerializerOptions 或 JsonTypeInfo 实例中指定的配置进行影响
JsonSerializerOptions options new(JsonSerializerOptions.Default)
{PropertyNamingPolicy JsonNamingPolicy.KebabCaseUpper,NumberHandling JsonNumberHandling.WriteAsString,UnmappedMemberHandling JsonUnmappedMemberHandling.Disallow,
};
JsonNode schema options.GetJsonSchemaAsNode(typeof(MyPoco));
Console.WriteLine(schema.ToString());
//{
// type: [object, null],
// properties: {
// NUMERIC-VALUE: {
// type: [string, integer],
// pattern: ^-?(?:0|[1-9]\\d*)$
// }
// },
// additionalProperties: false
//}
class MyPoco
{public int NumericValue { get; init; }
}用户可以使用 JsonSchemaExporterOptions 配置类型进一步控制生成的模式
JsonSerializerOptions options JsonSerializerOptions.Default;
JsonSchemaExporterOptions exporterOptions new()
{// 将根级类型标记为不可空TreatNullObliviousAsNonNullable true,
};
JsonNode schema options.GetJsonSchemaAsNode(typeof(Person), exporterOptions);
Console.WriteLine(schema.ToString());
//{
// type: object,
// properties: {
// Name: { type: string }
// },
// required: [Name]
//}
record Person(string Name);最后用户可以通过指定 TransformSchemaNode 委托, 对生成的架构节点应用自己的转换。以下是包含来自 DescriptionAttribute 注释的文本的示例
JsonSchemaExporterOptions exporterOptions new()
{TransformSchemaNode (context, schema) {// 确定类型或属性并提取相关属性提供程序ICustomAttributeProvider? attributeProvider context.PropertyInfo is not null? context.PropertyInfo.AttributeProvider: context.TypeInfo.Type;//查找任何描述属性DescriptionAttribute? descriptionAttr attributeProvider?.GetCustomAttributes(inherit: true).Select(attr attr as DescriptionAttribute).FirstOrDefault(attr attr is not null);//将描述属性应用于生成的架构if (descriptionAttr ! null){if (schema is not JsonObject jObj){// 处理架构为布尔值的情况JsonValueKind valueKind schema.GetValueKind();Debug.Assert(valueKind is JsonValueKind.True or JsonValueKind.False);schema jObj new JsonObject();if (valueKind is JsonValueKind.False){jObj.Add(not, true);}}jObj.Insert(0, description, descriptionAttr.Description);}return schema;}
};综合以上内容我们现在可以生成一个包含来自属性注释的 description 关键字源的模式
JsonNode schema options.GetJsonSchemaAsNode(typeof(Person), exporterOptions);
Console.WriteLine(schema.ToString());
//{
// description: A person,
// type: [object, null],
// properties: {
// Name: { description: The name of the person, type: string }
// },
// required: [Name]
//}
[Description(A person)]
record Person([property: Description(The name of the person)] string Name);在为 .NET 方法或 API 生成架构时这是一个特别有用的组件它被用于支持 ASP.NET Core 最新发布的 OpenAPI 组件并且我们已将其部署到许多具有工具调用要求的 AI 相关库和应用程序中例如 Semantic Kernel、Visual Studio Copilot 和最新发布的 Microsoft.Extensions.AI 库。
流式处理多个 JSON 文档
Utf8JsonReader 现在支持从单个缓冲区或流中读取多个以空格分隔的 JSON 文档。默认情况下如果 Utf8JsonReader 检测到第一个顶级文档后面有任何非空格字符它将抛出 异常。这可以使用 JsonReaderOptions.AllowMultipleValues 标志来改变
JsonReaderOptions options new() { AllowMultipleValues true };
Utf8JsonReader reader new(null {} 1 \r\n [1,2,3]u8, options);
reader.Read();
Console.WriteLine(reader.TokenType); // Null
reader.Read();
Console.WriteLine(reader.TokenType); // StartObject
reader.Skip();
reader.Read();
Console.WriteLine(reader.TokenType); // Number
reader.Read();
Console.WriteLine(reader.TokenType); // StartArray
reader.Skip();
Console.WriteLine(reader.Read()); // False此外这还使得从可能包含无效 JSON 尾部数据的有效负载中读取 JSON 成为可能
Utf8JsonReader reader new([1,2,3] NotJson/u8, new() { AllowMultipleValues true });
reader.Read();
reader.Skip(); // Success
reader.Read(); // throws JsonReaderExceptionUtf8JsonReader reader new([1,2,3] u8, new() { AllowMultipleValues true });在流式反序列化方面我们添加了新的 JsonSerializer.DeserializeAsyncEnumerable 重载使流式处理多个顶级值成为可能。默认情况下DeserializeAsyncEnumerable 将尝试流式处理顶级 JSON 数组中包含的元素。这可以使用新的 topLevelValues 标志切换
ReadOnlySpanbyte utf8Json [0] [0,1] [0,1,1] [0,1,1,2] [0,1,1,2,3]u8;
using var stream new MemoryStream(utf8Json.ToArray());
await foreach (int[] item in JsonSerializer.DeserializeAsyncEnumerableint[](stream, topLevelValues: true))
{Console.WriteLine(item.Length);
}遵循可 null 注释
JsonSerializer 现在为序列化和反序列化中的非空引用类型强制增加了有限的支持。这可以使用 RespectNullableAnnotations 标志进行切换
#nullable enable
JsonSerializerOptions options new() { RespectNullableAnnotations true };
MyPoco invalidValue new(Name: null!);
JsonSerializer.Serialize(invalidValue, options);
// System.Text.Json.JsonException类型“MyPoco”上的属性或字段“Name”不允许获取空值。//请考虑更新其可空性注释。
record MyPoco(string Name);同样此设置在反序列化时添加了强制执行
JsonSerializerOptions options new() { RespectNullableAnnotations true };
string json {Name:null};
JsonSerializer.DeserializeMyPoco(json, options);
// System.Text.Json.JsonException类型“MyPoco”上的构造函数参数“Name”不允许为空值。//请考虑更新其可空性注释。限制
由于非空引用类型的实现方式此功能带有一些重要的限制用户在启用之前需要熟悉这些限制。问题的根源在于引用类型可空性在 IL 中没有一流的表示形式例如从运行时反射的角度来看表达式 MyPoco 和 MyPoco? 是无法区分的。虽然编译器会尽可能通过发出属性元数据来弥补这一点但这仅限于特定类型定义范围内的非泛型成员注解。正是出于这个原因该标志仅验证非泛型属性、字段和构造函数参数上存在的可空性注释。System.Text.Json 不支持对
顶级类型也就是进行第一次 JsonSerializer.(De)serialize 调用时传递的类型。
集合元素类型也就是我们无法区分 List和 List类型。
任何通用的属性、字段或构造函数参数。
如果您希望在这些情况下强制执行可空性验证建议您将类型建模为 struct因为结构体不允许空值或者编写一个自定义转换器将其 HandleNull 属性重写为 true。
功能开关
用户可以使用 System.Text.Json.Serialization.RespectNullableAnnotationsDefault 功能开关全局打开 RespectNullableAnnotations 设置该开关可通过项目配置进行设置
ItemGroupRuntimeHostConfigurationOption IncludeSystem.Text.Json.Serialization.RespectNullableAnnotationsDefault Valuetrue /
/ItemGroup可空参数和可选参数之间的关系
需要注意的是RespectNullableAnnotations 不会将强制执行范围扩展到未指定的 JSON 值
JsonSerializerOptions options new() { RespectNullableAnnotations true };
var result JsonSerializer.DeserializeMyPoco({}, options); // No exception!
Console.WriteLine(result.Name is null); // True
class MyPoco
{public string Name { get; set; }
}这是因为 STJ 将必需属性和非可空属性视为正交概念。这源于 C# 语言本身在 C# 语言中您可以拥有可空的 required 属性
MyPoco poco new() { Value null }; // 没有编译器警告
class MyPoco
{public required string? Value { get; set; }
}以及不可为空的可选属性
class MyPoco
{public string Value { get; set; } default;
}同样的正交性也适用于构造函数参数
record MyPoco(string RequiredNonNullable,string? RequiredNullable,string OptionalNonNullable default,string? OptionalNullable default);遵循非可选的构造函数参数
基于 STJ 构造函数的反序列化历来将所有构造函数参数视为可选如以下示例中所示
var result JsonSerializer.DeserializePerson({});
Console.WriteLine(result); // Person { Name , Age 0 }
record Person(string Name, int Age);在 .NET 9 中我们包含了 RespectRequiredConstructorParameters 标志该标志会改变行为使得非可选的构造函数参数现在被视为必需的
JsonSerializerOptions options new() { RespectRequiredConstructorParameters true };
string json {Optional: value};
JsonSerializer.DeserializeMyPoco(json, options);
record MyPoco(string Required, string? Optional null);
// JsonException类型“MyPoco”的 JSON 反序列化缺少必需的属性包括“Required”。功能开关
用户可以使用 System.Text.Json.Serialization.RespectRequiredConstructorParametersDefault 功能开关全局打开 RespectRequiredConstructorParameters 设置该开关可通过项目配置进行设置
ItemGroupRuntimeHostConfigurationOption IncludeSystem.Text.Json.Serialization.RespectRequiredConstructorParametersDefault Valuetrue /
/ItemGroupRespectNullableAnnotations 和 RespectRequiredConstructorParameter 属性均作为可选标记实现以避免破坏现有应用程序。如果您正在编写新应用程序强烈建议您在代码中启用这两个标记。
自定义枚举成员名称
新的 JsonStringEnumMemberName 特性可以用来为作为字符串序列化的类型中的单个枚举成员自定义名称
JsonSerializer.Serialize(MyEnum.Value1 | MyEnum.Value2); // Value1, Custom enum value
[Flags, JsonConverter(typeof(JsonStringEnumConverter))]
enum MyEnum
{Value1 1,[JsonStringEnumMemberName(Custom enum value)]Value2 2,
}无序元数据读取
System.Text.Json 的某些功能例如多态性或 ReferenceHandler.Preserve需要在数据传输中发出元数据属性
JsonSerializerOptions options new() { ReferenceHandler ReferenceHandler.Preserve };
Base value new Derived(Name);
JsonSerializer.Serialize(value, options); // {$id:1,$type:derived,Name:Name}
[JsonDerivedType(typeof(Derived), derived)]
record Base;
record Derived(string Name) : Base;默认情况下STJ 元数据读取器要求元数据属性 $id 和 $type 必须在 JSON 对象的开始处定义
JsonSerializer.DeserializeBase({Name:Name,$type:derived});
// JsonException: The metadata property is either not supported by the
// type or is not the first property in the deserialized JSON object.众所周知当需要反序列化不是来自 System.Text.Json 的 JSON 有效负载时这会产生问题。可以配置新的 AllowOutOfOrderMetadataProperties 来禁用此限制
JsonSerializerOptions options new() { AllowOutOfOrderMetadataProperties true };
JsonSerializer.DeserializeBase({Name:Name,$type:derived}, options); // Success启用此标志时应小心谨慎因为在对非常大的 JSON 对象执行流式反序列化时它可能会导致缓冲过度和 OOM 故障。这是因为元数据属性必须在实例化反序列化对象之前读取这意味着所有位于 $type 属性之前的属性必须保留在缓冲区中以便后续的属性绑定。
自定义缩进
JsonWriterOptions 和 JsonSerializerOptions 类型现在公开了用于配置缩进的 API。以下示例启用了单制表符缩进
JsonSerializerOptions options new()
{WriteIndented true,IndentCharacter \t,IndentSize 1,
};
JsonSerializer.Serialize(new { Value 42 }, options);JsonObject 属性顺序操作
JsonObject 类型是可变 DOM 的一部分用于表示 JSON 对象。尽管该类型被建模为 IDictionary但它确实封装了用户不可修改的隐式属性顺序。新版本公开了其他 API可有效地将该类型建模为有序字典
public partial class JsonObject : IListKeyValuePairstring, JsonNode?
{public int IndexOf(string key);public void Insert(int index, string key, JsonNode? value);public void RemoveAt(int index);
}这允许修改可以直接影响属性顺序的对象实例
// 将 $id 属性添加或移动到对象的开头
var schema (JsonObject)JsonSerializerOptions.Default.GetJsonSchemaAsNode(typeof(MyPoco));
switch (schema.IndexOf($id, out JsonNode? idValue))
{case 0: // 缺少 $id 属性idValue (JsonNode)https://example.com/schema;schema.Insert(0, $id, idValue);break;case 0: // $id 属性已位于对象的开头break; case int index: //$id 存在但不在对象的开头schema.RemoveAt(index);schema.Insert(0, $id, idValue);
}JsonElement 和 JsonNode 中的 DeepEquals 方法
新的 JsonElement.DeepEquals 方法扩展了对 JsonElement 实例的深度相等比较补充了已有的 JsonNode.DeepEquals 方法。此外这两个方法在其实现中进行了改进例如处理等效 JSON 数字表示的方式
JsonElement left JsonDocument.Parse(10e-3).RootElement;
JsonElement right JsonDocument.Parse(0.001).RootElement;
JsonElement.DeepEquals(left, right); // TrueJsonSerializerOptions.Web
新的 JsonSerializerOptions.Web 单例可以使用 JsonSerializerDefaults.Web 设置快速序列化值
JsonSerializerOptions options JsonSerializerOptions.Web; // 用来代替 new(JsonSerializerDefaults.Web);
JsonSerializer.Serialize(new { Value 42 }, options); // {value:42}性能改进
有关 .NET 9 中 System.Text.Json 性能改进的详细说明请参阅 Stephen Toub 的“.NET 9 中的性能改进”文章中的相关部分。
结束语
.NET 9 拥有大量新功能和使用质量改进重点是 JSON 架构和智能应用程序支持。在 .NET 9 开发期间共有 46 个拉取请求为 System.Text.Json 做出贡献。我们希望您尝试新功能并向我们提供反馈告诉我们它如何改进您的应用程序以及您可能遇到的任何可用性问题或错误。
我们随时欢迎社区贡献。如果您想为 System.Text.Json 做出贡献请查看我们在 GitHub 上的求助问题列表。