Deep find or search the key at any level in JSON and replace its value in c#

Viewed 4612

I am also struck in a sort of problem. I am able to convert the nested JSON into key-Value , but Now I want to convert it into back its orignal json format. As of my problem I can't use the C# Object Model to do this, because the JSON file that I have is dynamic and its structure changes over the period of time. So I am looking for the solution by which we can serialize and deserialize the JSON via updated Key-Value pair. Any help would be great relief. TIA.

Sample JSON Code:

 {
  "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);

The output of this code is coming like this :

[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, ]

I want to convert it into back its original JSON structure.

1 Answers

The below ExtensionMethods can help you to update any key value in your json on any level.

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);
            }
        }
    }
}

And here I write one custom function that find the key and replace it's value,

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;
}

Important: What's the fourth parameter occurence mean here?

  • If you put null in this parameter then the value will be updated for all the occurrences of the specified key in json at any level.
  • If you put any index like 0, 1 then the value will be updated for the specified index of the specified key in json at any level.

And you can use it like

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();

Output:

enter image description here

Reference: Brian Rogers, senshin

Related