Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Editing JSON using JSONPath

Tags:

json

c#

jsonpath

Hello SO community :)!

I want to create method which will let user to edit (or add) JSON specific value or object (located in JSON by JSONPath). Below simple example what is on my mind. User always enters JSON, JSONPath and value/object to change. I'm using Json.NET library.

Method input {json, jsonpath, valuetoedit} || output {new json as string}

Example input:

{ "store": {
    "book": [ 
      { "category": "reference",
        "author": "Nigel Rees",
        "title": "Sayings of the Century",
        "price": 8.95
      },
      { "category": "fiction",
        "author": "Evelyn Waugh",
        "title": "Sword of Honour",
        "price": 12.99
      },
      { "category": "fiction",
        "author": "Herman Melville",
        "title": "Moby Dick",
        "isbn": "0-553-21311-3",
        "price": 8.99
      },
      { "category": "fiction",
        "author": "J. R. R. Tolkien",
        "title": "The Lord of the Rings",
        "isbn": "0-395-19395-8",
        "price": 22.99
      }
    ],
    "bicycle": {
      "color": "red",
      "price": 19.95
    }
  }
}

Example JSONPath:

$.store.book[*].author

Example value to change:

NewAuthorSpecifiedByUser

Output - new JSON where all authors will be changed to 'NewAuthorSpecifiedByUser'.

Is that even possible?

like image 726
James The Evangelist Avatar asked Mar 04 '16 14:03

James The Evangelist


1 Answers

The 3rd party package json.net allows this to be done easily:

  1. Parse the JSON into a LINQ to JSON hierarchy of JToken tokens.

  2. Select JSON values to modify via SelectTokens using a query string in JSONPath syntax.

  3. Using JToken.Replace() replace the selected values with the new values. A new value can be serialized directly to a JToken using JToken.FromObject().

Thus:

public static class JsonExtensions
{
    public static JToken ReplacePath<T>(this JToken root, string path, T newValue)
    {
        if (root == null || path == null)
            throw new ArgumentNullException();

        foreach (var value in root.SelectTokens(path).ToList())
        {
            if (value == root)
                root = JToken.FromObject(newValue);
            else
                value.Replace(JToken.FromObject(newValue));
        }

        return root;
    }    

    public static string ReplacePath<T>(string jsonString, string path, T newValue)
    {
        return JToken.Parse(jsonString).ReplacePath(path, newValue).ToString();
    }    
}

Then use it like:

var newJsonAuthorString = JsonExtensions.ReplacePath(jsonString, @"$.store.book[*].author", "NewAuthorSpecifiedByUser");

Prototype fiddle. If you are going to allow the user to do a series of edits, it will likely be more efficient to keep your JSON in a JToken hierarchy permanently rather that repeatedly converting from and to a string representation.

See also How to install JSON.NET using NuGet?.

like image 103
dbc Avatar answered Sep 19 '22 05:09

dbc