在C#中深度查找或搜索JSON中任意级别的键并替换其值

3
我也遇到了一个问题。我能够将嵌套的JSON转换为键值对,但现在我想将其转换回其原始的JSON格式。由于我的问题是JSON文件是动态的,并且其结构随时间而改变,因此我无法使用C#对象模型来解决这个问题。因此,我正在寻找一种通过更新的键值对序列化和反序列化JSON的解决方案。任何帮助都将是巨大的帮助。谢谢您!
样例JSON代码:
 {
  "firstName": "John",
  "lastName": "Smith",
  "isAlive": true,
  "age": 25,
  "address": {
  "streetAddress": "21 2nd Street",
  "city": "New York",
  "state": "NY",
  "postalCode": "10021-3100"
 },
"phoneNumbers": [
{
  "type": "home",
  "number": "212 555-1234"
 },
{
  "type": "office",
  "number": "646 555-4567"
},
{
  "type": "mobile",
  "number": "123 456-7890"
}
],
"children": [],
"spouse": null

}

var obj = JObject.Parse(json);

var result = obj.Descendants()
.OfType<JProperty>()
.Select(p => new KeyValuePair<string, object>(p.Path,
    p.Value.Type == JTokenType.Array || p.Value.Type == JTokenType.Object
        ? null : p.Value));

foreach (var kvp in result)
  Console.WriteLine(kvp);

这段代码的输出如下所示:
[firstName, John]
[lastName, Smith]
[isAlive, True]
[age, 25]
[address, ]
[address.streetAddress, 21 2nd Street]
[address.city, New York]
[address.state, NY]
[address.postalCode, 10021-3100]
[phoneNumbers, ]
[phoneNumbers[0].type, home]
[phoneNumbers[0].number, 212 555-1234]
[phoneNumbers[1].type, office]
[phoneNumbers[1].number, 646 555-4567]
[phoneNumbers[2].type, mobile]
[phoneNumbers[2].number, 123 456-7890]
[children, ]
[spouse, ]

我希望将其转换回原始的JSON结构。

1个回答

7
以下的ExtensionMethods可以帮助您在任何级别上更新JSON中的任何键值。
public static class JsonExtensions
{
    public static void SetByPath(this JToken obj, string path, JToken value)
    {
        JToken token = obj.SelectToken(path);
        token.Replace(value);
    }

    public static List<JToken> FindTokens(this JToken containerToken, string name)
    {
        List<JToken> matches = new List<JToken>();
        FindTokens(containerToken, name, matches);
        return matches;
    }

    private static void FindTokens(JToken containerToken, string name, List<JToken> matches)
    {
        if (containerToken.Type == JTokenType.Object)
        {
            foreach (JProperty child in containerToken.Children<JProperty>())
            {
                if (child.Name == name)
                {
                    matches.Add(child.Value);
                }
                FindTokens(child.Value, name, matches);
            }
        }
        else if (containerToken.Type == JTokenType.Array)
        {
            foreach (JToken child in containerToken.Children())
            {
                FindTokens(child, name, matches);
            }
        }
    }
}

这里我编写了一个自定义函数,用于查找键并替换其值

public static JToken FindAndReplace(JToken jToken, string key, JToken value, int? occurence)
{
    var searchedTokens = jToken.FindTokens(key);
    int count = searchedTokens.Count;

    if (count == 0)
        return $"The key you have to serach is not present in json, Key: {key}";

    foreach (JToken token in searchedTokens)
    {
        if (!occurence.HasValue)
            jToken.SetByPath(token.Path, value);
        else
        if (occurence.Value == searchedTokens.IndexOf(token))
            jToken.SetByPath(token.Path, value);
    }

    return jToken;
}

重要提示:这里的第四个参数occurence是什么意思?

  • 如果在此参数中放置null,则该值将更新为json中指定键的所有出现,无论在哪个级别。
  • 如果放置任何索引,如01,则该值将更新为json中指定键的指定索引,无论在哪个级别。

您可以像下面这样使用它

string json = File.ReadAllText(@"Path to your json file");

JToken jToken = JToken.Parse(json);

jToken = FindAndReplace(jToken, "firstName", "Matthew", null);
jToken = FindAndReplace(jToken, "lastName", "Gilbert", null);
jToken = FindAndReplace(jToken, "streetAddress", "Falcon Ave, 91 Street, New Jersey", null);
jToken = FindAndReplace(jToken, "postalCode", "R12H34", null);

jToken = FindAndReplace(jToken, "type", "work", 0);
jToken = FindAndReplace(jToken, "number", "787-878-7878", 0);

jToken = FindAndReplace(jToken, "type", "factory", 1);
jToken = FindAndReplace(jToken, "number", "989-898-9898", 1);

string outputJson = jToken.ToString();

输出:

在此输入图片描述

参考: Brian Rogers, senshin


我所拥有的JSON不是一致的,它可能会变化,因此键和数组不是静态的,它们可能会改变,那么我们该如何实现呢?因为我不能在我的cs文件中写任何硬编码。 - Pankaj Sharma
我们将如何遍历数组,例如myClass.DynamicData["phoneNumbers"][0]["type"],因为在我的情况下JSON文件是动态创建的。 - Pankaj Sharma
你的第二条评论的意思是,你必须对数组中的每个索引项使用上面的代码,对吗?这里的 phoneNumbers 是一个数组,你需要迭代这个数组。 - er-sho
如果我有两个或更多同名的键。 - Pankaj Sharma
在这种情况下,您需要提供“出现次数”参数来替换键的值。例如,如果您为键“Abc”提供“出现次数=2”,则从JSON的顶部到底部,我们的程序将搜索“第3个”键并替换其值。就像我上面为键“type”和“number”所做的那样。 - er-sho
显示剩余6条评论

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