JContainer、JObject、JToken 和 Linq 混淆

问题描述 投票:0回答:3

我无法理解何时使用

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"];

然后我会在

JObject
中输入
jsonObject1
。这是正确的方法吗?

我不清楚何时/为什么要使用

JContainer
,因为
JToken
JObject
对象似乎与 LINQ 配合得很好。
JContainer
的目的是什么?

c# json linq json.net
3个回答
422
投票

在大多数情况下,您真的不需要担心

JContainer
。它可以帮助将 LINQ-to-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
    属性来确定它是什么类型的令牌并对其进行适当的转换。

27
投票

JContainer
是具有子项的 JSON 元素的基类。
JObject
JArray
JProperty
JConstructor
都继承自它。

例如以下代码:

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

会抛出一个

InvalidCastException
,但如果你将它投射到
JContainer
,那就没问题了。

关于你原来的问题,如果你知道顶层有一个 JSON 对象,你可以使用:

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

1
投票

大多数示例都有简单的 JSON,我不止一次在 google 上搜索过“C# Newtonsoft parse 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);
© www.soinside.com 2019 - 2024. All rights reserved.