首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在使用自定义InputFormatter时使用配置的JSON选项?

如何在使用自定义InputFormatter时使用配置的JSON选项?
EN

Stack Overflow用户
提问于 2022-04-21 17:49:10
回答 1查看 203关注 0票数 1

我希望用自定义输入格式化程序替换默认的SystemTextJsonInputFormatter,或者作为TextInputFormatter的子类,或者(最好是) SystemTextJsonInputFormatter;最好是后者,尽可能接近内置的ASp.NET核心行为,而不必将代码复制到类中,在新的ASP.NET Core版本对SystemTextJsonInputFormatter进行更改时,需要定期更新。

这里有一些相关信息:

ASP.NET核心Web中的自定义格式化程序

提供如何注册自定义输入格式化程序的示例:

代码语言:javascript
复制
var builder = WebApplication.CreateBuilder(args);
   
builder.Services.AddControllers(options =>
{
    options.InputFormatters.Insert(0, new VcardInputFormatter());
});

但是,我目前也对JSON选项进行了自定义,如下所示:

代码语言:javascript
复制
internal static IServiceCollection ConfigureJsonSerialization(this IServiceCollection services)
{
    services.AddControllers().AddJsonOptions(opts => {
        // Note. some of these settings are defaults, but we ensure they are set as required.
        opts.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
        opts.JsonSerializerOptions.PropertyNameCaseInsensitive = false;
        opts.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;

        // Add our custom converters.
        opts.JsonSerializerOptions.Converters.Add(new DateOnlyJsonConverter());
    });

    return services;
}

opts有一个Microsoft.AspNetCore.Mvc.JsonOptions对象。

当使用内置SystemTextJsonInputFormatter时,这些选项通过其构造函数将其放入格式化程序实例(相关代码似乎在MvcCoreMvcOptionsSetup中)。

我希望在我的自定义输入格式化程序中使用相同的JSON选项,但是无法看到如何在构建时获得JsonOptions (从上面的options.InputFormatters.Insert(0, new VcardInputFormatter());行)。

我将继续查看ASP.NET核心内部,试图找出一种方法,但我想我会在这里要求,以防任何人可以提供一些指导。

谢谢。

EN

回答 1

Stack Overflow用户

发布于 2022-04-21 22:02:46

一种有点麻烦的方法是简单地从JsonSerializerOptions核心提供的现有SystemTextJsonInputFormatter实例中复制ASP.NET属性,如下所示:

代码语言:javascript
复制
public static class ServiceCollectionExtensions
{
    internal static IServiceCollection ConfigureJsonSerialization(this IServiceCollection services)
    {
        services.AddControllers(
            opts => {
                // Search for an existing instance of SystemTextJsonInputFormatter in the InputFormatters list;
                // this is the instance provided as standard by ASP.NET Core.
                int idx = FindSystemTextJsonInputFormatter(
                    opts.InputFormatters,
                    out SystemTextJsonInputFormatter? sysTextJsonInputFormatter);

                // We expect to find the SystemTextJsonInputFormatter.
                if (sysTextJsonInputFormatter is null)
                    throw new InvalidOperationException("No SystemTextJsonInputFormatter is defined.");

                // We will now create our own input formatter, and swap it in to substitute the
                // SystemTextJsonInputFormatter instance. First we have some objects to setup.

                // Copy the json serializer settings from the existing SystemTextJsonInputFormatter.
                var jsonOptions = new JsonOptions();
                CopyJsonSerializerOptions(
                    jsonOptions.JsonSerializerOptions,
                    sysTextJsonInputFormatter.SerializerOptions);

                // TODO/FIXME: Research how best to get a logger for our input formatter!
                var loggerFactory = LoggerFactory.Create(builder => {
                    builder.AddConsole();
                });
                var logger = loggerFactory.CreateLogger<SystemTextJsonInputFormatter>();

                // Perform the substitution.
                opts.InputFormatters[idx] = new JsonMergePatchInputFormatter(jsonOptions, logger);
            })
            .AddJsonOptions(opts => {
                // Note. some of these settings are defaults, but we ensure they are set as required.
                opts.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
                opts.JsonSerializerOptions.PropertyNameCaseInsensitive = false;
                opts.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;

                // Add our custom converters.
                opts.JsonSerializerOptions.Converters.Add(new DateOnlyJsonConverter());
            });

        return services;
    }

    private static int FindSystemTextJsonInputFormatter(
        FormatterCollection<IInputFormatter> inputFormatters,
        out SystemTextJsonInputFormatter? sysTextJsonInputFormatter)
    {
        // Search for an instance of SystemTextJsonInputFormatter, and store it's index in the InputFormatters list.
        // Note. We expect only one instance at most, but if there is more then one then we simply take the first instance.
        for (int i = 0; i < inputFormatters.Count; i++)
        {
            if (inputFormatters[i] is SystemTextJsonInputFormatter formatter)
            {
                sysTextJsonInputFormatter = formatter;
                return i;
            }
        }

        // Not found.
        sysTextJsonInputFormatter = null;
        return -1;
    }

    private static void CopyJsonSerializerOptions(JsonSerializerOptions targetOptions, JsonSerializerOptions sourceOptions)
    {
        // Notes. A hacky but necessary approach, because we are given a pre-built JsonSerializerOptions object
        // that can't be replaced, therefore we must copy the various setting into it.
        targetOptions.AllowTrailingCommas = sourceOptions.AllowTrailingCommas;
        targetOptions.DefaultBufferSize = sourceOptions.DefaultBufferSize;
        targetOptions.DefaultIgnoreCondition = sourceOptions.DefaultIgnoreCondition;
        targetOptions.DictionaryKeyPolicy = sourceOptions.DictionaryKeyPolicy;
        targetOptions.Encoder = sourceOptions.Encoder;
        targetOptions.IgnoreNullValues = sourceOptions.IgnoreNullValues;
        targetOptions.IgnoreReadOnlyFields = sourceOptions.IgnoreReadOnlyFields;
        targetOptions.IncludeFields = sourceOptions.IncludeFields;
        targetOptions.MaxDepth = sourceOptions.MaxDepth;
        targetOptions.NumberHandling = sourceOptions.NumberHandling;
        targetOptions.PropertyNameCaseInsensitive = sourceOptions.PropertyNameCaseInsensitive;
        targetOptions.PropertyNamingPolicy = sourceOptions.PropertyNamingPolicy;
        targetOptions.ReadCommentHandling = sourceOptions.ReadCommentHandling;
        targetOptions.ReferenceHandler = sourceOptions.ReferenceHandler;
        targetOptions.UnknownTypeHandling = sourceOptions.UnknownTypeHandling;
        targetOptions.WriteIndented = sourceOptions.WriteIndented;
        targetOptions.DefaultIgnoreCondition = sourceOptions.DefaultIgnoreCondition;
        targetOptions.DefaultIgnoreCondition = sourceOptions.DefaultIgnoreCondition;

        // Re-use the same converter instances; this is OK because their current parent
        // SystemTextJsonInputFormatter is about to be discarded.
        targetOptions.Converters.Clear();
        foreach (var jsonConverter in sourceOptions.Converters)
        {
            targetOptions.Converters.Add(jsonConverter);
        }
    }
}

我花了一些时间仔细研究ASP.NET核心源代码和调用堆栈,试图找到一种更好/更干净/更正式的方式,但最终不得不放弃并使用上面的方法。如果有更好的解决办法,请告诉我。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71958817

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档