将JSON驼峰大小写转换为蛇形大小写(反之亦然),并将数值字符串化

时间:2018-12-14 12:28:43

标签: c# json json.net camelcasing

我必须向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
        }]
    }
}

应该成为

{
    "first_object": {
        "nested_object": {
            "attribute_string": "ok",
            "attribute_numeric": "123.45"
        },
        "other_object": [{
            "array_val": "100"
        }, {
            "array_val": "200"
        }]
    }
}

我看到Json.Net库具有SnakeCaseNamingStrategyCamelCaseNamingStrategy类,所以这个想法是使用JsonTextReader来解析输入,更改属性名称的命名约定,将数字值设置为字符串,然后使用JsonTextWriter写入修改后的标记。

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

1 个答案:

答案 0 :(得分:2)

执行所需操作的最简单方法是使用一组与JSON匹配的模型类。 (您可以在Visual Studio中通过将JSON的完整示例复制到剪贴板,然后使用Edit -> Paste Special -> Paste JSON as Classes函数来生成类。)使模型类对属性名称使用大写驼峰(这是标准命名)还是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
}

然后,要将大驼峰式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);

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

要返回另一种方式,您可以这样做:

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

在这里,反序列化期间,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);
    }
}

要使用它,您只需要为输入JSON设置一个TextReader并为输出设置一个TextWriter,然后传递您想要用于转换。例如,要将原始JSON字符串转换为蛇形,请执行以下操作:

NamingStrategy

或者,如果JSON的源和/或目标是某种形式的流,则可以改用using (StringReader sr = new StringReader(upperCamelCaseJson)) using (StringWriter sw = new StringWriter()) { ConvertJson(sr, sw, new SnakeCaseConverter(), formatting); string snakeCaseJson = sw.ToString(); ... } / StreamReader

StreamWriter

现在,对于回程,存在一些问题。 using (StreamReader sr = new StreamReader(inputStream)) using (StreamWriter sw = new StreamWriter(outputStream)) { ConvertJson(sr, sw, new SnakeCaseConverter(), formatting); } 仅在一个方向上起作用;它没有提供逆转换的功能。这意味着,Newtonsoft提供的NamingStrategy类都无法以您想要的方式将蛇案转换为大骆驼案。 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(); } } 方法,以将蛇案JSON转换回大骆驼案。

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