将嵌套的JSON转换为简单的JSON

5

我希望通过递归遍历来将嵌套的json转换为简单的json。(输入json的结构未知)

例如,我想要这样的json:

{
    "FirstName": "Rahul",
    "LastName": "B",
    "EmpType": {
        "RID": 2,
        "Title": "Full Time"
    },
    "CTC": "3.5",
    "Exp": "1",
    "ComplexObj": {
        "RID": 3,
        "Title": {
            "Test": "RID",
            "TWO": {
                "Test": 12
            }
        }
    }
}

需要将其转换为类似于这样的内容

{
    "FirstName": "Rahul",
    "LastName": "B",
    "EmpType__RID": 2,
    "EmpType__Title": "Full Time",
    "CTC": "3.5",
    "Exp": "1",
    "ComplexObj__RID": 3,
    "ComplexObj__Title__Test": "RID",
    "ComplexObj__Title__TWO__Test": 12
}

嵌套对象中的每个字段都将被更改为代表其实际路径的键。

这是我目前所做的。

    public static void ConvertNestedJsonToSimpleJson(JObject jobject, ref JObject jobjectRef, string currentNodeName = "", string rootPath = "")
    {
        string propName = "";
        if (currentNodeName.Equals(rootPath))
        {
            propName = currentNodeName;
        }
        else
        {
            propName = (rootPath == "" && currentNodeName == "") ? rootPath + "" + currentNodeName : rootPath + "__" + currentNodeName;
        }

        foreach (JProperty jprop in jobject.Properties())
        {
            if (jprop.Children<JObject>().Count() == 0)
            {
                jobjectRef.Add(propName == "" ? jprop.Name : propName + "__" + jprop.Name, jprop.Value);
            }
            else
            {
                currentNodeName = jprop.Name;
                rootPath = rootPath == "" ? jprop.Name : rootPath;
                ConvertNestedJsonToSimpleJson(JObject.Parse(jprop.Value.ToString()), ref jobjectRef, currentNodeName, rootPath);
            }
        }
    }

并且得到错误的结果

{
    "FirstName": "Rahul",
    "LastName": "B",
    "EmpType__RID": 2,
    "EmpType__Title": "Full Time",
    "CTC": "3.5",
    "Exp": "1",
    "EmpType__ComplexObj__RID": 3,
    "EmpType__Title__Test": "RID",
    "EmpType__two__Test": 12
}

我希望您能帮助纠正我的代码,或提供其他实现该目标的方法。

1
你在使用Visual Studio吗?如果你将一个进程附加到你的程序上,你应该能够逐步查看问题所在。 - Alex
rootPath = rootPath == "" ? jprop.Name : rootPath;应用程序第一次执行此代码后,条件将始终为false。 - Tinwor
3个回答

5
  • 每次都将属性的值转换为字符串然后再解析是不必要的 - 只需要将其强制转换为JObject
  • 你不需要复杂的条件逻辑来生成属性名称 - 只需使用这个:prefix + jprop.Name + "__"

代码:

public static void FlattenJson(JObject node, JObject result, string prefix = "")
{
    foreach (var jprop in node.Properties())
    {
        if (jprop.Children<JObject>().Count() == 0)
        {
            result.Add(prefix + jprop.Name, jprop.Value);
        }
        else
        {
            FlattenJson((JObject)jprop.Value, $"{prefix}{jprop.Name}__", result);
        }
    }
}

您可以像这样调用它:
var node = JObject.Parse(/* the input string */);
var result = new JObject();
FlattenJson(node, result);

1
只是一个提示:更清晰的写法是 $"{prefix }{jprop.Name}__",其中 prefix + jprop.Name + "__" 是原始写法。 - Tinwor
@Tinwor 我不确定在这种情况下是否更清晰,但肯定更简洁。 - Botond Balázs
1
棒极了的代码!!可以从任何JSON创建数据表,还可以展开数组: if (jprop.Value.GetType() != typeof(JArray)) ... else {result.Add(prefix + "arr_" + jprop.Name, string.Join(",",jprop.Value));} - FabianSilva

4
您的问题出现在这一行代码中:rootPath = rootPath == "" ? jprop.Name : rootPath;。当您首次遇到EmpType时,您正在更改根路径,这意味着当您处理ComplexObj时,您的根路径已经不正确了。我认为您想要的只是更改递归函数中传入的内容。
不过,保持根和当前节点作为两个单独的项跟踪是不必要的。更好的做法是仅跟踪给定节点的当前前缀,代码看起来更像这样:
public static void ConvertNestedJsonToSimpleJson(JObject input, JObject output, string prefix = "")
{
    foreach (JProperty jprop in input.Properties())
    {
        var name = prefix==""?jprop.Name:String.Format("{0}__{1}", prefix,jprop.Name);
        if (jprop.Children<JObject>().Count() == 0)
        {
            output.Add(name, jprop.Value);
        }
        else
        {
            ConvertNestedJsonToSimpleJson((JObject)jprop.Value, output, name);
        }
    }
}

现在这给了我输出:

{
  "FirstName": "Rahul",
  "LastName": "B",
  "EmpType__RID": 2,
  "EmpType__Title": "Full Time",
  "CTC": "3.5",
  "Exp": "1",
  "ComplexObj__RID": 3,
  "ComplexObj__Title__Test": "RID",
  "ComplexObj__Title__TWO__Test": 12
}

看起来正确。


感谢指出错误。对于2级嵌套,输出JSON中的最后一个字段应为"ComplexObj__Title__TWO__Test": 12,现在已经正常工作。 - Rahul Bhosale
啊,是的。看起来问题出在递归函数的实现上有些混乱(根节点和当前节点的传递方式)。我强烈怀疑修复这段代码的最终版本会像Botond所写的那样简洁明了,因此我建议您暂时参考他的答案。 - Chris
我现在已经更新了我的答案,并提供了更简单的可工作代码。虽然不完全相同,但与Botond的代码非常相似。 - Chris

1
你能用LINQ做出类似这样的东西吗?
var jsonObj = jobject.select(x => new CustomJson {
   FirstName = x.FirstName,
   LastName = x.LastName,
   EmpTypeId = x.EmpType.Id,
   Title = x.EmpType.Title
   etc etc
});

是的,我可以这样做,但我的输入JSON动态结构是未知的。抱歉之前没有提到,已经编辑了我的帖子。 - Rahul Bhosale
好的,抱歉我现在不知道如何回答这个问题。 - Bad Dub

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接