我很难理解何时使用JContainer
、JObject
和JToken
。我从"标准"中了解到,JObject
由JProperties
组成,JToken
是所有JToken
类型的基本抽象类,但我不理解JContainer
。
我用的是C#,我刚买了LinqPad Pro 5。
我在一个文件中有一个JSON数据源,所以我正在使用以下语句成功地反序列化该文件的内容:
string json;
using (StreamReader reader = new StreamReader(@"myjsonfile.json"))
{
json = reader.ReadToEnd();
}
这时,我将JSON字符串对象反序列化为JObject
(这可能是我的错误——也许我需要将jsonWork
设置为JToken
或JContainer
?):
JObject jsonWork = (JObject)JsonConvert.DeserializeObject(json);
在我的JSON数据(JSON表示的字符串)中,我有三个对象——顶层对象看起来类似于:
{
"Object1" : { ... },
"Object2" : { ... },
"Object3" : { ... }
}
每个对象都由各种各样的令牌(数组、字符串、其他对象等)组成,因此它是动态JSON。(我使用省略号作为占位符,而不是用大量JSON数据混淆这个问题。)
但是,我想使用LINQ分别处理"Object1"
、"Object2"
和"Object3"
。所以,理想情况下,我想要这样的东西:
// these lines DO NOT work
var jsonObject1 = jsonWork.Children()["Object1"]
var jsonObject2 = jsonWork.Children()["Object2"]
var jsonObject3 = jsonWork.Children()["Object3"]
但以上几条线都失效了。
我在上面使用var
是因为我不知道应该使用什么对象类型:JContainer
、JObject
或JToken
!为了让你知道我想做什么,一旦上面的jsonObject#
变量被正确分配,我想使用LINQ来查询它们所包含的JSON。这里有一个非常简单的例子:
var query = from p in jsonObject1
where p.Name == "Name1"
select p
当然,我的LINQ最终会在jsonObject
变量中过滤JSON数组、对象、字符串等。我想一旦我开始,我就可以使用LinqPad来帮助我使用LINQ过滤JSON。
我发现如果我使用:
// this line WORKS
var jsonObject1 = ((JObject)jsonWork).["Object1"];
然后在jsonObject1
中得到一个JObject
类型。这是正确的方法吗?
当JToken
和JObject
对象与LINQ配合得很好时,我不清楚何时/为什么要使用JContainer
。JContainer
的用途是什么?
在大多数情况下,您实际上不需要担心JContainer
。它可以帮助将LINQ到JSON组织和结构化为经过良好分解的代码。
JToken
层次结构如下所示:
JToken - abstract base class
JContainer - abstract base class of JTokens that can contain other JTokens
JArray - represents a JSON array (contains an ordered list of JTokens)
JObject - represents a JSON object (contains a collection of JProperties)
JProperty - represents a JSON property (a name/JToken pair inside a JObject)
JValue - represents a primitive JSON value (string, number, boolean, null)
因此,您可以看到,JObject
是JContainer
,而则是JToken
。
以下是基本经验法则:
- 如果您知道您有一个对象(在JSON中用大括号
{
和}
表示),请使用JObject
- 如果您知道您有一个数组或列表(用方括号
[
和]
表示),请使用JArray
- 如果您知道您有一个基元值,请使用
JValue
- 如果您不知道自己拥有哪种令牌,或者希望能够以通用方式处理以上任何一种令牌,请使用
JToken
。然后,您可以检查其Type
属性,以确定它是什么类型的令牌,并对其进行适当的强制转换
JContainer
是具有子项的JSON元素的基类。JObject
、JArray
、JProperty
和JConstructor
都继承了它
例如,以下代码:
(JObject)JsonConvert.DeserializeObject("[1, 2, 3]")
会抛出一个InvalidCastException
,但如果将其强制转换为JContainer
,那就没问题了。
关于你最初的问题,如果你知道你有一个顶级的JSON对象,你可以使用:
var jsonWork = JObject.Parse(json);
var jsonObject1 = jsonWork["Object1"];
大多数示例都有简单的json,我已经在谷歌上搜索了不止一次"C#Newtonsoft解析json"。
这里有一个json文件,我刚刚被要求解析csv。公司名称值嵌套在许多数组/对象中,因此在这方面它是半复杂的。
{
"page": {
"page": 1,
"pageSize": 250
},
"dataRows": [
{
"columnValues": {
"companyName": [
{
"name": "My Awesome Company",
}
]
}
}
]
}
var jsonFilePath = @"C:data.json";
var jsonStr = File.ReadAllText(jsonFilePath);
// JObject implementation for getting dataRows JArray - in this case I find it simpler and more readable to use a dynamic cast (below)
//JObject jsonObj = JsonConvert.DeserializeObject<JObject>(jsonStr);
//var dataRows = (JArray)jsonObj["dataRows"];
var dataRows = ((dynamic)JsonConvert.DeserializeObject(jsonStr)).dataRows;
var csvLines = new List<string>();
for (var i = 0; i < dataRows.Count; i++)
{
var name = dataRows[i]["columnValues"]["companyName"][0]["name"].ToString();
// dynamic casting implemntation to get name - in this case, using JObject indexing (above) seems easier
//var name2 = ((dynamic)((dynamic)((dynamic)dataRows[i]).columnValues).companyName[0]).name.ToString();
csvLines.Add(name);
}
File.WriteAllLines($@"C:data_{DateTime.Now.Ticks}.csv", csvLines);