JToken is not a reference of JObject?

匆匆过客 提交于 2019-12-06 10:07:22

问题


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?


回答1:


First, let's talk about what a JToken is.

  • JToken is the abstract base class for JObject, JArray, JProperty and JValue.
  • JObject is a collection of JProperty objects. A JObject cannot hold any other kind of JToken.
  • JProperty is a name-value pair. The name is always a string, and the value can be any kind of JToken except another JProperty.
  • JArray is an array of JToken objects of any kind except JProperty.
  • JValue represents a JSON primitive value. It can contain a string, number, boolean, date or null. Note that JValue 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




回答2:


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!