Getting error Newtonsoft.Json.Linq.JProperty cannot have multiple values when adding JToken

Viewed 640

I have the following structure of additional information where I need to update the value of one of the tokens in the structure. The data is an array of JTokens with a parent called 'additionalFields' as follows:

{{"additionalFields": 
[
{ "name": "NAME1", "value": "VALUE1" },
{ "name": "NAME2", "value": "VALUE2" },
{ "name": "NAME3", "value": "VALUE3" },
{ "name": "NAME4", "value": "VALUE4" }
]}

I'm trying to update the value of one of the tokens e.g. to change VALUE1 to VALUE10.

Once I have located the token I need to update my code removes it as follows.

additionalField.Remove();

I then create a new token to replace the one I have removed (containing the new value) using the following functions.

public static JToken CreateNewToken(string name, string value)
{
    var stringToken = CreateNewStringToken(name, value);
    var token = JToken.Parse(stringToken);
     return (JToken) token;
}

private static string CreateNewStringToken(string name, string value)
{
    return $"{{\"name\":\"{name}\",\"value\":\"{value}\"}}";
}

I then add the new token as follows.

additionalFields.AddAfterSelf(updatedToken);

Putting it all together we have the following

foreach (var additionalField in additionalFields)
{
    //is this the key we are looking for?
    var keyToken = additionalField.First;

    if (keyToken?.First == null) continue;
    if (string.Equals(keyToken.First.ToString(), "newname", StringComparison.CurrentCultureIgnoreCase))
    {
        //remove the current token
        additionalField.Remove();
        
        //add the updated token
        var updatedToken = CreateNewToken("newname", "newvalue");
        additionalFields.AddAfterSelf(updatedToken); <-- error occurs here!!
    }
}

However after adding the token I'm getting the following error

Newtonsoft.Json.Linq.JProperty cannot have multiple values

I can see in the debugger that the token has been removed (as the token.Count is reduced by 1) so cannot understand why I'm getting an error adding the replacement token.

1 Answers

I was able to reproduce your problem here: https://dotnetfiddle.net/JIVCVB

What is going wrong

additionalFields refers to the JArray of JObjects containing name and value JProperties. You are looping through this JArray to try to find the first JObject having a name property with a certain value, and when you find it you attempt to replace the JObject with a whole new JObject. You successfully remove the old JObject from the JArray, but when you are doing AddAfterSelf to insert the new JObject, you are referencing additionalFields (plural) not additionalField (singular). Recall that additionalFields is the JArray. So you are saying that you want to add the new JObject after the array. The array's parent is a JProperty called additionalFields. A JProperty can only have one value, so AddAfterSelf fails with the error you see.

How to fix your code

I think what you intended to do was additionalField.AddAfterSelf(updatedToken). However, this, too, will fail, for a different reason: you already removed the additionalField from the JArray at that point, so it no longer has a parent context. You would need to AddAfterSelf before you remove the item you are trying to insert after. If you fix that, you still have another problem: your loop doesn't break out after you've done the replacement, so then you will get an error about modifying the collection while looping over it.

Here is the relevant section of code with the corrections:

if (string.Equals(keyToken.First.ToString(), "NAME1", StringComparison.CurrentCultureIgnoreCase))
{
    //add the updated token
    var updatedToken = CreateNewToken("newname", "newvalue");
    additionalField.AddAfterSelf(updatedToken);

    //remove the current token
    additionalField.Remove();
    
    // we found what we were looking for so no need to continue looping
    break; 
}

Fiddle: https://dotnetfiddle.net/KcFsZc

A simpler approach

You seem to be jumping through a lot of hoops to accomplish this task. Instead of looping, you can use FirstOrDefault to find the object you are looking for in the array. Once you've found it, you don't need to replace the whole object; you can just update the property values directly.

Here's how:

var rootObject = JToken.Parse(json);

// Get a reference to the array of objects as before
var additionalFields = rootObject["additionalFields"];

// Find the object we need to change in the array
var additionalField = additionalFields.FirstOrDefault(f => 
    string.Equals((string)f["name"], "NAME1", StringComparison.CurrentCultureIgnoreCase);

// if the object is found, update its properties
if (additionalField != null)
{
    additionalField["name"] = "newname";
    additionalField["value"] = "newvalue";
}

Working demo: https://dotnetfiddle.net/ZAKRmi

Related