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" }}'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.