将平面JSON转换为嵌套JSON

时间:2019-02-22 00:55:21

标签: c# json algorithm

我有一个 flat JSON,如下所示(我不知道该怎么称呼,希望 flat 是正确的词)

{
    "id":12947,
    "name.first_name":"Honey",
    "address.street.number":"23",
    "address.city.code":"LL",
    "address.street.name":"Narrow Street",
    "address.city.name":"Lalaland",
    "name.last_name":"Moon",
    "books": [
        {
            "title":"The long story",
            "author.first_name":"Brew",
            "author.last_name":"Beating",
            "type":"novel"
        },        
        {
            "title":"Money and morality",
            "author.first_name":"Chris",
            "author.last_name":"Mas",
            "type":"self-help"
        }
    ]
}

请注意,这些字段未按顺序排列。

我想将其转换为嵌套的JSON,如下所示:

{
    "id":12947,
    "name":{
        "first_name":"Honey",
        "last_name":"Moon"
    },
    "address":{
        "street":{
            "number":"23",
            "name":"Narrow Street"
        },    
        "city":{
            "code":"LL",
            "name":"Lalaland"
        }
    },
    "books": [
        {
            "title":"The long story",
            "author": {
                "first_name":"Brew",
                "last_name":"Beating"
            },
            "type":"novel"
        },        
        {
            "title":"Money and morality",
            "author":{
                "first_name":"Chris",
                "last_name":"Mas"
            },
            "type":"self-help"
        }
    ]
}

有什么好的算法可以转换它?

我是C#用户,我打算使用Newtonsoft.Json将输入的JSON解析为JObject,然后遍历所有字段以检查其键并创建嵌套的JObjects。对于数组,我对每个数组项重复相同的过程。

您有更好的主意吗?

1 个答案:

答案 0 :(得分:1)

这是我对有兴趣的人的解决方案。

public static string ConvertFlatJson(string input)
{
    var token = JToken.Parse(input);

    if (token is JObject obj)
    {
        return ConvertJObject(obj).ToString();
    }

    if (token is JArray array)
    {
        return ConvertArray(array).ToString();
    }

    return input;
}

private static JObject ConvertJObject(JObject input)
{
    var enumerable = ((IEnumerable<KeyValuePair<string, JToken>>)input).OrderBy(kvp => kvp.Key);

    var result = new JObject();
    foreach (var outerField in enumerable)
    {
        var key = outerField.Key;
        var value = outerField.Value;

        if (value is JArray array)
        {
            value = ConvertArray(array);
        }

        var fieldNames = key.Split('.');
        var currentObj = result;

        for (var fieldNameIndex = 0; fieldNameIndex < fieldNames.Length; fieldNameIndex++)
        {
            var fieldName = fieldNames[fieldNameIndex];

            if (fieldNameIndex == fieldNames.Length - 1)
            {
                currentObj[fieldName] = value;
                continue;
            }

            if (currentObj.ContainsKey(fieldName))
            {
                currentObj = (JObject)currentObj[fieldName];
                continue;
            }

            var newObj = new JObject();
            currentObj[fieldName] = newObj;
            currentObj = newObj;
        }
    }

    return result;
}

private static JArray ConvertArray(JArray array)
{
    var resultArray = new JArray();

    foreach (var arrayItem in array)
    {
        if (!(arrayItem is JObject))
        {
            resultArray.Add(arrayItem);
            continue;
        }

        var itemObj = (JObject)arrayItem;

        resultArray.Add(ConvertJObject(itemObj));
    }

    return resultArray;
}