c# - 如何从 Utf8JsonReader 获取属性路径?

标签 c# asp.net-mvc asp.net-core system.text.json

使用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://learn.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://learn.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://learn.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"));
        }
    }
}

如何访问当前路径,以便抛出同时包含自定义消息和 Path 的异常从我的自定义 JsonConverterRead() 方法中?

最佳答案

JsonConverter 内,您希望使用自定义消息引发自定义异常,并包含 JSONPath 信息。正如 docs 所解释的, System.Text.Json仅将路径信息附加到 JsonException 类型的异常中- 仅当异常没有消息时。那么,如何包含路径信息呢?

最明显的方法是从 JsonConverter<T>.Read() 中获取当前路径。并将其传递给异常的构造函数。不幸的是,System.Text.Json不使路径可用于 Read() Write() 。这可以通过检查reference source来确认。 。 <强> Utf8JsonReader 目前甚至不知道路径。它所知道的只是使用 BitStack 的容器类型(对象或数组)堆栈。成员(member) Utf8JsonReader._bitStack ,这是退出嵌套容器时正确处理其状态转换所需的最低限度。 JsonSerializer 确实通过 ReadStack 跟踪当前堆栈ref 结构体有 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://learn.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; }
    }
    
  • 您可能会考虑解析您的 DateTime使用 CultureInfo.InvariantCulture :

    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 .

关于c# - 如何从 Utf8JsonReader 获取属性路径?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69136510/

相关文章:

c# - 当我们在 Entity Framework Core 中拥有主键时,我们是否应该始终使用 .Find() 而不是 .FirstOrDefault() ?

c# - 扩展 Label 控件 - 添加 Click 事件处理程序

c# - 我正在使用 log4net 进行日志记录,但它没有写入日志

c# - Entity Framework 4.3.1 始终在更新数据库上运行所有迁移

asp.net-mvc - 如何将 Azure Web 应用服务拆分为多个 'sub web app services' ?

c# - rc1.final 中的 User.Identity 中不存在 getUserId

c# - 通过 WPF 应用程序启动时 Winform 窗口会缩小

javascript - ASP.NET MVC/Javascript : VideoJS media could not be loaded

javascript - 在没有 openFile 对话框的情况下将文件从客户端上传到服务器

c# - 在 DbContext.OnConfiguring 和 AspCore Startup.ConfigureServices 中定义 optionsBuilder 时的预期结果是什么?