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 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
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
Find Out Username(Who) Modified File in C#
Auto Create Database in Entity Framework Core
How to Get the Kestrel Web Server to Listen to Non-Localhost Requests
Embed Unity3D App Inside Wpf Application
Automatic Native and Managed Dlls Extracting from Nuget Package
How to Receive Plug & Play Device Notifications Without a Windows Form
Anonymous Types - Are There Any Distingushing Characteristics
Onewaytosource Binding Seems Broken in .Net 4.0
Wpf Change Background Color of a Combobox
Accessing Properties Through Generic Type Parameter
Convert Word Doc and Docx Format to PDF in .Net Core Without Microsoft.Office.Interop
Differencebetween Casting and Conversion
How to Check Whether an Object Has Certain Method/Property
Async/Await Different Thread Id
How to Hide "Chrome Is Being Controlled by Automated Software" Infobar Within Chrome V76