在ASP.NET Core 3中是否有使用蛇形大小写作为JSON命名策略的内置方法?

时间:2019-10-26 11:05:32

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

我设法使用以下代码使其工作:

.AddNewtonsoftJson(options => {
    options.SerializerSettings.ContractResolver = new DefaultContractResolver
    {
        NamingStrategy = new SnakeCaseNamingStrategy()
    };
});

但是,这使得MVC使用Newtonsoft而不是System.Text.JSON,它是更快,异步且内置的。

看看System.Text.JSON中的命名策略选项,我只能找到CamelCase。蛇案有本地支持吗?实现蛇案JSON命名风格的更好方法是什么?

6 个答案:

答案 0 :(得分:2)

目前尚不支持蛇案,
.NET Core 3.0允许通过继承JsonNamingPolicy来建立自定义命名策略

您需要通过蛇​​形转换实现ConvertName方法。
(Newtonsoft Json.NET有一个内部StringUtils类,显示了如何处理此问题。)


下面的POC实现仅将Json.NET的SnakeCaseNamingStrategy用于蛇格转换(,而整个应用程序都使用System.Text.Json

最好避免仅对蛇格转换依赖Newtonsoft Json.Net,但是在下面的这个LAZY示例中,我不想重新思考/重新发明蛇格转换方法。
这个答案的重点是如何挂钩自定义策略(而不是蛇格转换本身)。
(有很多库和code samples都显示了如何做到这一点。)< / em>

public class SnakeCaseNamingPolicy : JsonNamingPolicy
{
    private readonly SnakeCaseNamingStrategy _newtonsoftSnakeCaseNamingStrategy
        = new SnakeCaseNamingStrategy();

    public static SnakeCaseNamingPolicy Instance { get; } = new SnakeCaseNamingPolicy();

    public override string ConvertName(string name)
    { 
        /* A conversion to snake case implementation goes here. */

        return _newtonsoftSnakeCaseNamingStrategy.GetPropertyName(name, false);
    }
}

Startup.cs中,您将应用此自定义SnakeCaseNamingPolicy

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers()            
        .AddJsonOptions(
            options => { 
                options.JsonSerializerOptions.PropertyNamingPolicy = 
                    SnakeCaseNamingPolicy.Instance;
            });
}

下面的类的一个实例

public class WeatherForecast
{
    public DateTime Date { get; set; }

    public int TemperatureCelcius { get; set; }

    public int TemperatureFahrenheit { get; set; }

    [JsonPropertyName("Description")]
    public string Summary { get; set; }
}

Json表示为:

{ "date" : "2019-10-28T01:00:56.6498885+01:00",
  "temperature_celcius" : 48,
  "temperature_fahrenheit" : 118,
  "Description" : "Cool"
}

请注意,属性Summary的名称为Description
与其System.Text.Json.Serialization.JsonPropertyNameAttribute匹配。

答案 1 :(得分:1)

我在这里分享@pfx解决方案的完整实现。自定义命名策略(从NewtonSoft复制):

@Module

在简单的示例中使用以下模型:

@Module(includes = XXXModule)

示例用法:

using System.Text;
using System.Text.Json;

namespace Utils
{
    public class SnakeCaseNamingPolicy : JsonNamingPolicy
    {
        public override string ConvertName(string name) => JsonUtils.ToSnakeCase(name);
    }

    public class JsonUtils
    {

        private enum SeparatedCaseState
        {
            Start,
            Lower,
            Upper,
            NewWord
        }

        public static string ToSnakeCase(string s) => ToSeparatedCase(s, '_');
    
        private static string ToSeparatedCase(string s, char separator)
        {
            if (string.IsNullOrEmpty(s))
            {
                return s;
            }

            StringBuilder sb = new StringBuilder();
            SeparatedCaseState state = SeparatedCaseState.Start;

            for (int i = 0; i < s.Length; i++)
            {
                if (s[i] == ' ')
                {
                    if (state != SeparatedCaseState.Start)
                    {
                        state = SeparatedCaseState.NewWord;
                    }
                }
                else if (char.IsUpper(s[i]))
                {
                    switch (state)
                    {
                        case SeparatedCaseState.Upper:
                            bool hasNext = (i + 1 < s.Length);
                            if (i > 0 && hasNext)
                            {
                                char nextChar = s[i + 1];
                                if (!char.IsUpper(nextChar) && nextChar != separator)
                                {
                                    sb.Append(separator);
                                }
                            }
                            break;
                        case SeparatedCaseState.Lower:
                        case SeparatedCaseState.NewWord:
                            sb.Append(separator);
                            break;
                    }

                    char c;
                    c = char.ToLowerInvariant(s[i]);
                    sb.Append(c);

                    state = SeparatedCaseState.Upper;
                }
                else if (s[i] == separator)
                {
                    sb.Append(separator);
                    state = SeparatedCaseState.Start;
                }
                else
                {
                    if (state == SeparatedCaseState.NewWord)
                    {
                        sb.Append(separator);
                    }

                    sb.Append(s[i]);
                    state = SeparatedCaseState.Lower;
                }
            }

            return sb.ToString();
        }
    }
}

给予 public class TestSerializer { public DateTime TimeStamp { get; set; } public int CPUPower { get; set; } }

答案 2 :(得分:1)

在这里您可以找到很好的解释以及性能比较。 希望性能比较能够解决您的“ 什么是更好的实现方式”部分的问题。

https://www.michaelrose.dev/system-text-json-snakecase

注意:在Microsoft于 2019年9月前后,这是一个较旧的解决方案 没有开始调查它。现在他们已经在处理它,但是还没有将其解决方案发布到最新版本。但是,您可以访问下面的链接,以查看DotNet官方git repo的代码(可能是潜在的内置解决方案)。

https://github.com/database64128/shadowsocks-uri-generator/commit/e8d1517c35acdc897decdbfc98fd83b81712c954

答案 3 :(得分:0)

只需对 pfx 代码进行一些修改,以消除对Newtonsoft Json.Net的依赖。

String扩展名键,用于将给定的字符串转换为SnakeCase

public static class StringUtils
{
    public static string ToSnakeCase(this string str)
    {
        return string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToLower();
    }
}

然后在我们的SnakeCaseNamingPolicy中可以做到

public class SnakeCaseNamingPolicy : JsonNamingPolicy
{
    public static SnakeCaseNamingPolicy Instance { get; } = new SnakeCaseNamingPolicy();

    public override string ConvertName(string name)
    {
        // Conversion to other naming conventaion goes here. Like SnakeCase, KebabCase etc.
        return name.ToSnakeCase();
    }
}

最后一步是在Startup.cs

中注册我们的命名政策
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers()            
        .AddJsonOptions(
            options => { 
                options.JsonSerializerOptions.PropertyNamingPolicy = 
                    SnakeCaseNamingPolicy.Instance;
            });
}

使用模型:

public class WeatherForecast
{
    public DateTime Date { get; set; }

    public int TemperatureCelcius { get; set; }

    public int TemperatureFahrenheit { get; set; }

    public string Summary { get; set; }
}

Json输出:

{
  "date": "2019-10-28T08:26:14.878444+05:00",
  "temperature_celcius": 4,
  "temperature_fahrenheit": 0,
  "summary": "Scorching"
}

答案 4 :(得分:0)

有点晚了,但是这个解决方案也将解决像ABCItem或MyCPU这样的情况。 这只是概念,您可以对其进行改进以使其更加通用

using System.Collections.Generic;

namespace Extensions
{
    public static class StringExtension
    {
        public static string ToSnakeCase(this string str)
        {
            // collect the final result
            var snakeCase = new List<char>();

            // check and add chars (using for loop for performance)
            for (int i = 0; i < str.Length; i++)
            {
                if (i > 0 && char.IsUpper(str[i]) && !char.IsUpper(str[i + 1]))
                {
                    snakeCase.Add('_');
                }
                snakeCase.Add(str[i]);
            }

            // build the new string
            return new string(snakeCase.ToArray()).ToLower();
        }
    }
}

var snakeCase = "CPUMeter".ToSnakeCase();
var snakeCase = "PascalCase".ToSnakeCase();
var snakeCase = "camelCase".ToSnakeCase();

答案 5 :(得分:0)

这是一个带有 SnakeCaseKebabCase NamingPolicy for System.Text.Json

的存储库

https://github.com/J0rgeSerran0/JsonNamingPolicy

JsonSnakeCaseNamingPolicy

var options = new JsonSerializerOptions() { PropertyNamingPolicy = new JsonSnakeCaseNamingPolicy() };
var person = new Person() { FirstName = "Jorge", Birthday = DateTime.UtcNow, MyJobCity = "Madrid" };
var json = JsonSerializer.Serialize(person, options);

JsonKebabCaseNamingPolicy

var options = new JsonSerializerOptions() { PropertyNamingPolicy = new JsonKebabCaseNamingPolicy() };
var person = new Person() { FirstName = "Jorge", Birthday = DateTime.UtcNow, MyJobCity = "Madrid" };
var json = JsonSerializer.Serialize(person, options);