如何从 Utf8JsonReader 获取 属性 路径?
How do I get the property path from Utf8JsonReader?
使用 NewtonSoft,我们可以通过 reader.Path
获取路径。 System.Text.Json没有这个。
namespace API.JsonConverters
{
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
/// <summary>
/// Use DateTime.Parse to replicate how Newtonsoft worked.
/// </summary>
/// <remarks>https://docs.microsoft.com/en-us/dotnet/standard/datetime/system-text-json-support</remarks>
public class DateTimeConverterUsingDateTimeParse : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
try
{
return DateTime.Parse(reader.GetString(), styles: System.Globalization.DateTimeStyles.RoundtripKind);
}
catch (FormatException)
{
// We have to do this to have the Path variable auto populated so when the middleware catches the error, it will properly populate the ModelState errors.
// https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to#error-handling
// https://github.com/dotnet/aspnetcore/blob/release/3.1/src/Mvc/Mvc.Core/src/Formatters/SystemTextJsonInputFormatter.cs#L79
throw new JsonException("Invalid DateTime. Please use RoundTripKind (MM-DD-YYYY) - https://docs.microsoft.com/en-us/dotnet/standard/base-types/how-to-round-trip-date-and-time-values");
}
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString("o"));
}
}
}
我怎样才能访问当前路径,以便我可以从自定义 [=15= 的 Read()
方法中抛出一个包含自定义消息和 Path
的异常]?
在 JsonConverter
, you want to throw a custom exception with a custom message and also include JSONPath information. As explained by the docs, System.Text.Json
only appends path information to an exception of type JsonException
内 - 并且仅当异常没有消息时。那么,怎么才能包含路径信息呢?
执行此操作的明显方法是从 JsonConverter<T>.Read()
and pass it to your exception's constructor. Unfortunately, System.Text.Json
does not make the path available to Read()
or Write()
. This can be confirmed by checking the reference source. Utf8JsonReader
currently does not even know the path. All it knows is the stack of container types (object or array) using a BitStack
member Utf8JsonReader._bitStack
, which is the minimum necessary to handle its state transitions correctly when exiting a nested container. JsonSerializer
does track the current stack, via the ReadStack
ref struct which has a JsonPath()
方法中获取当前路径。不幸的是 ReadStack
是内部的,永远不会暴露给应用程序或 Utf8JsonReader
.
作为解决方法,您可以创建嵌套异常,其中内部异常是您所需的异常类型,外部异常是 JsonException
,序列化程序将在其中填充路径。以下是如何执行此操作的一个示例:
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
try
{
return DateTime.Parse(reader.GetString(),
styles: System.Globalization.DateTimeStyles.RoundtripKind);
}
catch (FormatException)
{
var innerEx = new ProblemDetailsException("Invalid DateTime. Please use RoundTripKind (MM-DD-YYYY) - https://docs.microsoft.com/en-us/dotnet/standard/base-types/how-to-round-trip-date-and-time-values");
throw new JsonException(null, innerEx);
}
}
然后在更高的层次上你可以抓住 JsonException
并抛出一个外部 ProblemDetailsException
,例如像这样:
public class JsonExtensions
{
public static T Deserialize<T>(string json, JsonSerializerOptions options = default)
{
try
{
return JsonSerializer.Deserialize<T>(json, options);
}
catch (JsonException ex) when (ex.InnerException is ProblemDetailsException innerException)
{
var finalException = new ProblemDetailsException(innerException.Message, ex.Path, ex);
throw finalException;
}
}
}
备注:
这里我假设 ProblemDetailsException
看起来像:
public class ProblemDetailsException : System.Exception
{
public ProblemDetailsException(string message) : base(message) { }
public ProblemDetailsException(string message, Exception inner) : base(message, inner) { }
public ProblemDetailsException(string message, string path) : base(message) => this.Path = path;
public ProblemDetailsException(string message, string path, Exception inner) : base(message, inner) => this.Path = path;
public string Path { get; }
}
您可以考虑使用 CultureInfo.InvariantCulture
:
解析您的 DateTime
return DateTime.Parse(reader.GetString(),
styles: System.Globalization.DateTimeStyles.RoundtripKind,
provider: System.Globalization.CultureInfo.InvariantCulture);
如目前所写,您的转换器在不同区域设置中的功能会有所不同。或者,如果您真的想在当前语言环境中进行解析,请在您的代码中明确说明:
return DateTime.Parse(reader.GetString(),
styles: System.Globalization.DateTimeStyles.RoundtripKind,
provider: System.Globalization.CultureInfo.CurrentCulture);
演示 fiddle here.
使用 NewtonSoft,我们可以通过 reader.Path
获取路径。 System.Text.Json没有这个。
namespace API.JsonConverters
{
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
/// <summary>
/// Use DateTime.Parse to replicate how Newtonsoft worked.
/// </summary>
/// <remarks>https://docs.microsoft.com/en-us/dotnet/standard/datetime/system-text-json-support</remarks>
public class DateTimeConverterUsingDateTimeParse : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
try
{
return DateTime.Parse(reader.GetString(), styles: System.Globalization.DateTimeStyles.RoundtripKind);
}
catch (FormatException)
{
// We have to do this to have the Path variable auto populated so when the middleware catches the error, it will properly populate the ModelState errors.
// https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to#error-handling
// https://github.com/dotnet/aspnetcore/blob/release/3.1/src/Mvc/Mvc.Core/src/Formatters/SystemTextJsonInputFormatter.cs#L79
throw new JsonException("Invalid DateTime. Please use RoundTripKind (MM-DD-YYYY) - https://docs.microsoft.com/en-us/dotnet/standard/base-types/how-to-round-trip-date-and-time-values");
}
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString("o"));
}
}
}
我怎样才能访问当前路径,以便我可以从自定义 [=15= 的 Read()
方法中抛出一个包含自定义消息和 Path
的异常]?
在 JsonConverter
, you want to throw a custom exception with a custom message and also include JSONPath information. As explained by the docs, System.Text.Json
only appends path information to an exception of type JsonException
内 - 并且仅当异常没有消息时。那么,怎么才能包含路径信息呢?
执行此操作的明显方法是从 JsonConverter<T>.Read()
and pass it to your exception's constructor. Unfortunately, System.Text.Json
does not make the path available to Read()
or Write()
. This can be confirmed by checking the reference source. Utf8JsonReader
currently does not even know the path. All it knows is the stack of container types (object or array) using a BitStack
member Utf8JsonReader._bitStack
, which is the minimum necessary to handle its state transitions correctly when exiting a nested container. JsonSerializer
does track the current stack, via the ReadStack
ref struct which has a JsonPath()
方法中获取当前路径。不幸的是 ReadStack
是内部的,永远不会暴露给应用程序或 Utf8JsonReader
.
作为解决方法,您可以创建嵌套异常,其中内部异常是您所需的异常类型,外部异常是 JsonException
,序列化程序将在其中填充路径。以下是如何执行此操作的一个示例:
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
try
{
return DateTime.Parse(reader.GetString(),
styles: System.Globalization.DateTimeStyles.RoundtripKind);
}
catch (FormatException)
{
var innerEx = new ProblemDetailsException("Invalid DateTime. Please use RoundTripKind (MM-DD-YYYY) - https://docs.microsoft.com/en-us/dotnet/standard/base-types/how-to-round-trip-date-and-time-values");
throw new JsonException(null, innerEx);
}
}
然后在更高的层次上你可以抓住 JsonException
并抛出一个外部 ProblemDetailsException
,例如像这样:
public class JsonExtensions
{
public static T Deserialize<T>(string json, JsonSerializerOptions options = default)
{
try
{
return JsonSerializer.Deserialize<T>(json, options);
}
catch (JsonException ex) when (ex.InnerException is ProblemDetailsException innerException)
{
var finalException = new ProblemDetailsException(innerException.Message, ex.Path, ex);
throw finalException;
}
}
}
备注:
这里我假设
ProblemDetailsException
看起来像:public class ProblemDetailsException : System.Exception { public ProblemDetailsException(string message) : base(message) { } public ProblemDetailsException(string message, Exception inner) : base(message, inner) { } public ProblemDetailsException(string message, string path) : base(message) => this.Path = path; public ProblemDetailsException(string message, string path, Exception inner) : base(message, inner) => this.Path = path; public string Path { get; } }
您可以考虑使用
解析您的CultureInfo.InvariantCulture
:DateTime
return DateTime.Parse(reader.GetString(), styles: System.Globalization.DateTimeStyles.RoundtripKind, provider: System.Globalization.CultureInfo.InvariantCulture);
如目前所写,您的转换器在不同区域设置中的功能会有所不同。或者,如果您真的想在当前语言环境中进行解析,请在您的代码中明确说明:
return DateTime.Parse(reader.GetString(), styles: System.Globalization.DateTimeStyles.RoundtripKind, provider: System.Globalization.CultureInfo.CurrentCulture);
演示 fiddle here.