I have not yet noticed that James Newton King wrote or spoke about what JToken
is. I have made the incorrect assumption that it somehow holds a reference to JObject
. This is not the case as the these LINQPad statements demonstrate:
var json = @"
{
""item"": {
""foo"": ""4"",
""bar"": ""42""
}
}
";
var jO = JObject.Parse(json);
var jToken = jO["item"]["foo"];
jToken = "5";
jO.ToString().Dump("jO");
jToken.Dump("jToken");
The output:
jO
{
"item": {
"foo": "4",
"bar": "42"
}
}
jToken
5
Should not jO["item"]["foo"] == 5
?
First, let's talk about what a JToken
is.
JToken
is the abstract base class forJObject
,JArray
,JProperty
andJValue
.JObject
is a collection ofJProperty
objects. AJObject
cannot hold any other kind ofJToken
.JProperty
is a name-value pair. The name is always a string, and the value can be any kind ofJToken
except anotherJProperty
.JArray
is an array ofJToken
objects of any kind exceptJProperty
.JValue
represents a JSON primitive value. It can contain a string, number, boolean, date or null. Note thatJValue
is a reference type like all other JTokens.
The above classes are intended to model the JSON spec.
Now let's talk about what you're doing and where you're getting confused.
In your code, you are first creating a JObject. The JObject contains one JProperty called item
. The value of item
is another JObject which contains two JProperties, called foo
and bar
. The values of these JProperties are both JValues containing strings (4
and 42
, respectively).
Next, you use the JToken indexer syntax to get a reference to the value of the foo
JProperty (a JValue which contains the string value 4
) and assign that reference to your jToken
variable. Note the declared type of this variable is JToken, even though the actual type of the value here is in fact JValue. (You can see this if you do jToken.GetType().Name.Dump("jToken type")
)
With me so far?
OK, here is where I think you are getting confused. JToken provides implicit and explicit conversions which allow it to be assigned from or cast to various .NET primitives. If you do jToken = "5"
that really means the same thing as jToken = new JValue("5")
. So what you have done is to replace the reference that your jToken
variable had (to the JValue containing 4
) with a new reference to a different JValue containing 5
. This obviously will have no effect on the original JObject.
If you are trying to modify the value of the original JValue, you need to cast your jToken
to JValue and then use the Value
setter to set it.
((JValue)jToken).Value = "5";
Fiddle: https://dotnetfiddle.net/StIGxM
Actually JToken
has a reference to its parent (check Parent
property).
Back to your example - in this line jToken = "5";
you are creating new JToken
(to be more specific, string is implicitly converted to JValue
). Basically it is the same as jToken = new JValue("5");
So variable jToken
is now pointing to brand new JValue
. This explains why the change is not reflected in original JObject
.
To fix your example use: ((JValue)jToken).Value = "5";
来源:https://stackoverflow.com/questions/38005957/jtoken-is-not-a-reference-of-jobject