Jcontainer, Jobject, Jtoken and Linq Confusion

JContainer, JObject, JToken and Linq confusion

JContainer is a base class for JSON elements that have child items. JObject, JArray, JProperty and JConstructor all inherit from it.

For example, the following code:

(JObject)JsonConvert.DeserializeObject("[1, 2, 3]")

Would throw an InvalidCastException, but if you cast it to a JContainer, it would be fine.

Regarding your original question, if you know you have a JSON object at the top level, you can just use:

var jsonWork = JObject.Parse(json);
var jsonObject1 = jsonWork["Object1"];

Cannot add new elements to Json Array

You are getting that error because the JObject indexer Parameters["Hardware"] is declared to return an object of type JToken:

public JToken this[
string propertyName
] { get; set; }

And JToken, which is the abstract base class for JArray, JObject, JValue and so on, has no Add() method. You need to downcast to JArray, which does have an Add() method, like so

((JArray)Parameters["Hardware"]).Add(Aux); 

Since c# is (mostly) statically typed and the indexer is declared to return JToken you can only use the methods of the abstract base class JToken. You must downcast to JArray to use JArray methods.

For a discussion of the LINQ to JSON type hierarchy see JContainer, JObject, JToken and Linq confusion.

Getting 'Cannot cast Newtonsoft.Json.Linq.JObject to Newtonsoft.Json.Linq.JToken' when retrieving items from JSON

You're trying to access datatype_properties as if it's an array. It's not - it's another object with a property country_code_iso3166_alpha3 which has an array value.

You can call the Value method with a JObject type argument to get the object, then Value again with a JArray type argument to get the array. Here's a short but complete example:

using System;
using System.Linq;
using Newtonsoft.Json.Linq;

class Test
{
static void Main()
{
string json = @"{
'datatype_properties': {
'country_name_iso3166_short': [
'Text Text'
]
}
}".Replace("'", "\"");
JObject parent = JObject.Parse(json);
string countryName = parent["datatype_properties"]
.Value<JArray>("country_name_iso3166_short")
.Values<string>()
.FirstOrDefault();
Console.WriteLine(countryName);
}
}

JToken is not a reference of JObject?

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

Add a JToken to a specific JsonPath in a JObject

As we mentioned on comment section, you can not add/replace item to index if index does not exist. In that case you need to add item as a new member of JArray instead, or you can use Insert to add item at the specified index :

JObject value = new JObject();
value.Add("addressLine1", "123");
value.Add("addressLine2", "144");

JObject o = JObject.Parse(json);
int index = 2;
JToken token = o.SelectToken("address[" + index + "]");
if (token != null && token.Type != JTokenType.Null)
{
token.Replace(value);
}
else //If index does not exist than add to Array
{
JArray jsonArray = (JArray)o["address"];
jsonArray.Add(value);
//jsonArray.Insert(index, value); Or you can use Insert

}

Parsing JObject properly

you have list of lists , not just list

List<List<float>> xyz = resultantData["data"]["xyz"].ToObject<List<List<float>>>();
List<List<int>> xy = resultantData["data"]["xy"].ToObject<List<List<int>>>();

you can use it like this

if (xyz != null && xyz.Count > 0)
{
List<float> xyz1 = xyz[0];
}

if you want to put everything in one pile, I don't think that it is a good idea , you have to try this

List<float> xyz = resultantData["data"]["xyz"].ToObject<List<List<float>>>()
.SelectMany(i=> i).ToList();
List<int> xy = resultantData["data"]["xy"].ToObject<List<List<int>>>()
.SelectMany(i=> i).ToList();


Related Topics



Leave a reply



Submit