0

I want to add a json property (name and value) or json data(array) to existing json string.

User has to specify a json path to specify where to add it.

Can someone help me to provide a link/example to progress on this.

Regards, Amar

3 Answers 3

2

If you want to set a existed place of path, you can use SelectToken:

void SetValueByPath(JToken token, string path, object value)
{
  var newToken = value == null ? null : JToken.FromObject(value);
  var targetToken = token.SelectToken(path);
  if(targetToken.Type == JTokenType.Property)
    targetToken.Replace(newToken)
}

But if you want to set a not existed place of path, here is the code:

//Origin code by Squirrel.Downy(Flithor)
public static void AddTokenByPath(JToken jToken, string path, object value)
{
    // "a.b.d[1]['my1.2.4'][4].af['micor.a.ee.f'].ra[6]"
    var pathParts = Regex.Split(path, @"(?=\[)|(?=\[\.)|(?<=])(?>\.)")
        // > { "a.b.d", "[1]", "['my1.2.4']", "[4]", "af", "['micor.a.ee.f']", "ra", "[6]" }
        .SelectMany(str => str.StartsWith("[") ? new[] { str } : str.Split('.'))
        // > { "a", "b", "d", "[1]", "['my1.2.4']", "[4]", "af", "['micor.a.ee.f']", "ra", "[6]" }
        .ToArray();
    JToken node = jToken;
    for (int i = 0; i < pathParts.Length; i++)
    {
        var pathPart = pathParts[i];
        var partNode = node.SelectToken(pathPart);
        //node is null or token with null value
        if (partNode == null || partNode.Type == JTokenType.Null)
        {
            if (i < pathParts.Length - 1)
            {
                //the next level is array or object
                //accept [0], not ['prop']
                JToken nextToken = Regex.IsMatch(pathParts[i + 1], @"\[\d+\]") ?
                    new JArray() : new JObject();
                SetToken(node, pathPart, nextToken);
            }
            else if (i == pathParts.Length - 1)
            {
                //JToken.FromObject(null) will throw a exception
                var jValue = value == null ?
                   null : JToken.FromObject(value);
                SetToken(node, pathPart, jValue);
            }
            partNode = node.SelectToken(pathPart);
        }
        node = partNode;
    }
    //set new token
    void SetToken(JToken node, string pathPart, JToken jToken)
    {
        if (node.Type == JTokenType.Object)
        {
            //get real prop name (convert "['prop']" to "prop")
            var name = pathPart.Trim('[', ']', '\'');
            ((JObject)node).Add(name, jToken);
        }
        else if (node.Type == JTokenType.Array)
        {
            //get real index (convert "[0]" to 0)
            var index = int.Parse(pathPart.Trim('[', ']'));
            var jArray = (JArray)node;
            //if index is bigger than array length, fill the array
            while (index >= jArray.Count)
                jArray.Add(null);
            //set token
            jArray[index] = jToken;
        }
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks but there is a slight issue. a.b.d should be parsed to ['a']['b']['d'] not ['a.b.d']
1

EDIT: After google'ing a bit I noticed .SelectToken()! This is exacly what I was thinking about when mentioning XPath.

// Inpup JSON
string input = "{ body: { name: { firstname: 'John', lastname: 'Doe' }, age: 43 } }";
JToken json = JToken.Parse(input); // Parsed to JToken as type may not be known.

// Select token based on JSONPath, see: http://goessner.net/articles/JsonPath/
JToken nameToken = json.SelectToken("$['body']['name']");
nameToken["middlename"] = "something";

// Prints: {"body":{"name":{"firstname":"John","lastname":"Doe","middlename":"something"},"age":43}}
string output = json.ToString(Newtonsoft.Json.Formatting.None);

OLD:
It seems like you want something like XPath for Json. So you can find an existing object/array and add to that?

My advice would be to search for an existing path tool for Json. Here's a quick and dirty example of how you may do it:

static void Main(string[] args)
    {
        string input = "{ body: { name: { firstname: 'John', lastname: 'Doe' }, age: 43 } }";

        JToken json = JToken.Parse(input);

        UpdateJson(json, "body/name/middlename", "Something");

        // {"body":{"name":{"firstname":"John","lastname":"Doe","middlename":"Something"},"age":43}}
        string output = json.ToString(Newtonsoft.Json.Formatting.None);

        UpdateJson(json, "body/jobs", new JArray(){ "C# Dev", "Network Master" });

        // {"body":{"name":{"firstname":"John","lastname":"Doe","middlename":"Something"},"age":43,"jobs":["C# Dev","Network Master"]}}
        string output2 = json.ToString(Newtonsoft.Json.Formatting.None);
    }

    private static void UpdateJson(JToken source, string path, JToken value)
    {
        UpdateJsonInternal(source, path.Split('/'), 0, value);
    }

    private static void UpdateJsonInternal(JToken source, string[] path, int pathIndex, JToken value)
    {
        if (pathIndex == path.Length - 1)
        {
            if (source is JArray)
            {
                ((JArray)source)[int.Parse(path[pathIndex])] = value;
            }
            else if (source is JObject)
            {
                ((JObject)source)[path[pathIndex]] = value;
            }
        }
        else if (source is JArray)
        {
            UpdateJsonInternal(((JArray)source)[int.Parse(path[pathIndex])], path, pathIndex + 1, value);
        }
        else if (source is JObject)
        {
            UpdateJsonInternal(((JObject)source)[path[pathIndex]], path, pathIndex + 1, value);
        }
    }
}

This adds or update the source with the JToken value at the path specified. So 'body/name/middlename' either adds 'middlename' to 'name' or updates it with 'value'. If 'name' does not exist, this example simply fails.

4 Comments

Thanks Peter for quick response.
Thanks Peter and Kevin for quick response. Instead of iterating, how can I get the JObject from the path body/name, so that I can set jObject["middlename"]="something"; As per my requirement, user has to input path, property. Here property can be a name and value pair or a json data itself. so I need jObject[property]=value;
Okay, I googled a bit and noticed .SelectToken(). This function allows you to select a json token based on a path. The path you can read more about on: goessner.net/articles/JsonPath I've edited my answer with a simple example
Peter, great thanks. How can I append another json object/data into existing json string. Without setting property, I want to append json string into source json by using json path
1

You can just use Newtonsoft.Json:

        var input = "{ test: true }";

        var jObject = JObject.Parse(input);

        jObject["updated"] = true;
        jObject["array"] = new JArray("item1", "item2", "item3");

        var s = jObject.ToString();

        // { test: true, updated: true, array: ["item1", "item2", "item3"] }
        Console.WriteLine(s);

Above we've parsed the json string in to a JObject then with that JObject we can start to modify it by adding fields etc.. then to get back the string representation we just call ToString on the JObject.

3 Comments

Thanks Kevin. is there any way setting json value without property name, just append to the existing json
@AmarTa do you mean merge 2 json documents together?
@KevinSmith can i create JObject from JSON path, I have a JSON path '$.someObject.someProperty' and wanted to create an Ojbect which parse into a string like this '{ "someObject" : { "someProperty" : "some value" }}'

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.