11

I have a string representing JSON and I want to rename some of the properties using JSON.NET. I need a generic function to use for any JSON. Something like:

public static void Rename(JContainer container, Dictiontionary<string, string> mapping)
{
  foreach (JToken el in container.Children())
  {
    JProperty p = el as JProperty;
    if(el != null && mapping.ContainsKey(p.Name))
    {
      // **RENAME THIS NODE!!**
    }

    // recursively rename nodes
    JContainer pcont = el as JContainer;
    if(pcont != null)
    {
      Rename(pcont, mapping);
    }
  }
}

How to do it??

Dmitrii Lobanov
  • 4,897
  • 1
  • 33
  • 50
Mohamed Nuur
  • 5,536
  • 6
  • 39
  • 55

2 Answers2

19

I would suggest reconstructing your JSON with renamed properties. I don't think you should worry about speed penalties as it's usually not an issue. Here's how you can do it.

public static JToken Rename(JToken json, Dictionary<string, string> map)
{
    return Rename(json, name => map.ContainsKey(name) ? map[name] : name);
}

public static JToken Rename(JToken json, Func<string, string> map)
{
    JProperty prop = json as JProperty;
    if (prop != null) 
    {
        return new JProperty(map(prop.Name), Rename(prop.Value, map));
    }

    JArray arr = json as JArray;
    if (arr != null)
    {
        var cont = arr.Select(el => Rename(el, map));
        return new JArray(cont);
    }

    JObject o = json as JObject;
    if (o != null)
    {
        var cont = o.Properties().Select(el => Rename(el, map));
        return new JObject(cont);
    }

    return json;
}

And here's an example of usage:

var s = @"{ ""A"": { ""B"": 1, ""Test"": ""123"", ""C"": { ""Test"": [ ""1"", ""2"", ""3"" ] } } }";
var json = JObject.Parse(s);

var renamed = Rename(json, name => name == "Test" ? "TestRenamed" : name);
renamed.ToString().Dump();  // LINQPad output

var dict = new Dictionary<string, string> { { "Test", "TestRenamed"} };
var renamedDict = Rename(json, dict);
renamedDict.ToString().Dump();  // LINQPad output
Dmitrii Lobanov
  • 4,897
  • 1
  • 33
  • 50
5

We use this approach. You can find the property you want using JObject's SelectToken(). Yes it does support JsonPath.

public static class NewtonsoftExtensions
{
    public static void Rename(this JToken token, string newName)
    {
        var parent = token.Parent;
        if (parent == null)
            throw new InvalidOperationException("The parent is missing.");
        var newToken = new JProperty(newName, token);
        parent.Replace(newToken);
    }
}
Abi Bellamkonda
  • 137
  • 2
  • 5
  • Slightly surprised you didn't point out that this is actually part of the framework, landed by you: https://github.com/abibell/Newtonsoft.Json/commit/12f3f32401074a84b3d186956e59e56c9e8781cb :-) – Alastair Maw Mar 17 '16 at 10:27
  • 1
    Hi Alastair, I wanted the feature. I made coffee changes for it and sent a pull request. It was not accepted by the owner with a reason "I don't like it".https://github.com/JamesNK/Newtonsoft.Json/pull/549 – Abi Bellamkonda Mar 18 '16 at 19:37
  • 1
    @AbiBellamkonda, I'm trying to use this extension method to rename a JProperty object, but I get the exception "Can not add Newtonsoft.Json.Linq.JProperty to Newtonsoft.Json.Linq.JProperty." from this bit of code: `new JProperty(newName, token)`. – Gyromite Apr 18 '17 at 18:33
  • Hi Gyromite, Sorry, I want to help but I can't as I am not following the library for a while now. You may have to look if the library changed the creation of JProperty in the recent versions? – Abi Bellamkonda Apr 21 '17 at 00:32
  • I get the same error as @Gyromite. This code does not work. – Chris Jul 26 '19 at 13:10