将 JSON 驼峰案例转换为蛇案例(反之亦然)并字符串化数值

dav*_*rey 4 c# json camelcasing json.net

我必须向 Web REST 服务发送和接收 JSON 对象。这些对象由一个 DLL 生成,该 DLL 将大写字母(“PropertyName”)中的属性名称序列化,并且 Web 服务需要蛇形大小写(“property_name”)。另外,DLL 将数值序列化为浮点数,但 REST API 需要所有字符串。处理对象后,REST 服务返回蛇形大小写 JSON。

JSON 很复杂并且包含嵌套的数组和对象。在从 REST 字符串转换回来时,我可以跳过数字字符串的去字符串化,但我仍然必须将属性名称重新转换为大写字母。

我正在考虑使用 Newtonsoft Json 库编写一个辅助类,但它看起来比我预期的要棘手。转换器应接受 JSON 并返回 JSON。

例子:

{
    "FirstObject": {
        "NestedObject": {
            "AttributeString": "ok",
            "AttributeNumeric": 123.45
        },
        "OtherObject": [{
            "ArrayVal": 100
        }, {
            "ArrayVal": 200
        }]
    }
}
Run Code Online (Sandbox Code Playgroud)

应该成为

{
    "first_object": {
        "nested_object": {
            "attribute_string": "ok",
            "attribute_numeric": "123.45"
        },
        "other_object": [{
            "array_val": "100"
        }, {
            "array_val": "200"
        }]
    }
}
Run Code Online (Sandbox Code Playgroud)

我看到 Json.Net 库有SnakeCaseNamingStrategyCamelCaseNamingStrategy类,所以想法是使用 aJsonTextReader来解析输入,更改属性名称的命名约定,将数值设置为字符串,并使用JsonTextWriter.

我找不到有关如何执行此操作的任何示例。

Bri*_*ers 7

做你想做的最简单的方法是使用一组与你的 JSON 匹配的模型类。(您可以通过将 JSON 的完整示例复制到剪贴板,然后使用该Edit -> Paste Special -> Paste JSON as Classes函数来在 Visual Studio 中生成类。)使模型类对属性名称使用大写驼峰(无论如何,这是 C# 的标准命名约定) , 并使用字符串代替数字属性。

因此,对于您的示例 JSON,模型类将如下所示:

public class RootObject
{
    public FirstObject FirstObject { get; set; }
}

public class FirstObject
{
    public NestedObject NestedObject { get; set; }
    public List<OtherObject> OtherObject { get; set; }
}

public class NestedObject
{
    public string AttributeString { get; set; }
    public string AttributeNumeric { get; set; }
}

public class OtherObject
{
    public string ArrayVal { get; set; }  // using string instead of int here
}
Run Code Online (Sandbox Code Playgroud)

然后,要将驼峰式大写 JSON 转换为蛇式大写,您可以执行以下操作:

var obj = JsonConvert.DeserializeObject<RootObject>(json);
var settings = new JsonSerializerSettings
{
    ContractResolver = new DefaultContractResolver
    {
        NamingStrategy = new SnakeCaseNamingStrategy { ProcessDictionaryKeys = true }
    },
    Formatting = Formatting.Indented
};
json = JsonConvert.SerializeObject(obj, settings);
Run Code Online (Sandbox Code Playgroud)

原始 JSON 将自然地反序列化为模型,因为属性名称已经匹配。Json.Net 将根据需要自动将 JSON 中的数值转换为字符串以适合类属性。在序列化时,SnakeCaseNamingStrategy开始将属性名称更改为蛇形大小写。数值作为字符串写出,因为这是在类中声明属性的方式。

要以另一种方式返回,您将执行以下操作:

obj = JsonConvert.DeserializeObject<RootObject>(json, settings);  // same settings as above
json = JsonConvert.SerializeObject(obj, Formatting.Indented);
Run Code Online (Sandbox Code Playgroud)

在这里,在反序列化期间,Json.Net 使用SnakeCaseNamingStrategy将模型属性名称再次转换为蛇形大小写,以将它们与 JSON 属性匹配。数值已经是 JSON 中的字符串,因此不需要转换。在序列化时,我们没有使用任何特殊设置,因此属性名称完全按照声明的方式写出,这是大写的驼峰式。保存数值的字符串属性仍然是字符串(您在问题中说这没问题)。

这是一个往返演示:https : //dotnetfiddle.net/3Pb1fT


如果您没有可以使用的模型,仍然可以使用您建议的JsonReader/JsonWriter方法进行此转换,但是将它们粘合在一起并进行转换需要更多代码。这是一个辅助方法,可以完成大部分繁重的工作:

public static void ConvertJson(TextReader textReader, TextWriter textWriter, 
                               NamingStrategy strategy, 
                               Formatting formatting = Formatting.Indented)
{
    using (JsonReader reader = new JsonTextReader(textReader))
    using (JsonWriter writer = new JsonTextWriter(textWriter))
    {
        writer.Formatting = formatting;
        if (reader.TokenType == JsonToken.None)
        {
            reader.Read();
            ConvertJsonValue(reader, writer, strategy);
        }
    }
}

private static void ConvertJsonValue(JsonReader reader, JsonWriter writer, 
                                     NamingStrategy strategy)
{
    if (reader.TokenType == JsonToken.StartObject)
    {
        writer.WriteStartObject();
        while (reader.Read() && reader.TokenType != JsonToken.EndObject)
        {
            string name = strategy.GetPropertyName((string)reader.Value, false);
            writer.WritePropertyName(name);
            reader.Read();
            ConvertJsonValue(reader, writer, strategy);
        }
        writer.WriteEndObject();
    }
    else if (reader.TokenType == JsonToken.StartArray)
    {
        writer.WriteStartArray();
        while (reader.Read() && reader.TokenType != JsonToken.EndArray)
        {
            ConvertJsonValue(reader, writer, strategy);
        }
        writer.WriteEndArray();
    }
    else if (reader.TokenType == JsonToken.Integer)
    {
        // convert integer values to string
        writer.WriteValue(Convert.ToString((long)reader.Value));
    }
    else if (reader.TokenType == JsonToken.Float)
    {
        // convert floating point values to string
        writer.WriteValue(Convert.ToString((double)reader.Value,
                          System.Globalization.CultureInfo.InvariantCulture));        
    }
    else // string, bool, date, etc.
    {
        writer.WriteValue(reader.Value);
    }
}
Run Code Online (Sandbox Code Playgroud)

要使用它,您只需要TextReader为您的输入 JSON 和TextWriter输出设置一个,并传入NamingStrategy您想要用于转换的适当内容。例如,要将原始 JSON 字符串转换为蛇形大小写,您可以这样做:

using (StringReader sr = new StringReader(upperCamelCaseJson))
using (StringWriter sw = new StringWriter())
{
    ConvertJson(sr, sw, new SnakeCaseNamingStrategy(), formatting);
    string snakeCaseJson = sw.ToString();
    ...
}
Run Code Online (Sandbox Code Playgroud)

或者,如果 JSON 的源和/或目标是某种流,则可以使用StreamReader/StreamWriter代替:

using (StreamReader sr = new StreamReader(inputStream))
using (StreamWriter sw = new StreamWriter(outputStream))
{
    ConvertJson(sr, sw, new SnakeCaseNamingStrategy(), formatting);
}
Run Code Online (Sandbox Code Playgroud)

现在,对于回程,有一点问题。ANamingStrategy只对一个方向起作用;它不提供反转转换的工具。这意味着 Newtonsoft 提供的任何NamingStrategy类都无法按照您想要的方式将蛇形外壳转换回驼色外壳。该CamelCaseNamingStrategy不会工作,因为它不希望开始与蛇的情况下(就是了上骆驼的情况下),并且其输出不上骆驼反正。这DefaultNamingStrategy也不起作用,因为它实际上根本不进行任何转换——它只是一个传递。

解决方案是制作您自己的自定义NamingStrategy. 幸运的是,这并不难做到。只需从基NamingStrategy类派生并实现抽象ResolvePropertyName方法:

// This naming strategy converts snake case names to upper camel case (a.k.a. proper case)
public class ProperCaseFromSnakeCaseNamingStrategy : NamingStrategy
{
    protected override string ResolvePropertyName(string name)
    {
        StringBuilder sb = new StringBuilder(name.Length);
        for (int i = 0; i < name.Length; i++)
        {
            char c = name[i];

            if (i == 0 || name[i - 1] == '_')
                c = char.ToUpper(c);

            if (c != '_')
                sb.Append(c);
        }
        return sb.ToString();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以将这个新策略传递给上述ConvertJson方法,以将蛇形大小写 JSON 转换回驼峰大写。

往返演示:https : //dotnetfiddle.net/jt0XKD