【问题标题】:JContainer, JObject, JToken and Linq confusionJContainer、JObject、JToken和Linq混淆
【发布时间】:2016-11-28 06:28:32
【问题描述】:

我无法理解何时使用 JContainerJObjectJToken。我从“标准”中了解到JObjectJProperties 组成,JToken 是所有JToken 类型的基本抽象类,但我不明白JContainer

我正在使用 C#,我刚刚购买了 LinqPad Pro 5。

我在一个文件中有一个 JSON 数据源,因此我正在使用以下语句成功地反序列化该文件的内容:

string json;
using (StreamReader reader = new StreamReader(@"myjsonfile.json"))
{
    json = reader.ReadToEnd();
}

此时,我将 JSON 字符串对象反序列化为 JObject(这可能是我的错误——也许我需要将 jsonWork 设置为 JTokenJContainer?):

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,因为我不知道应该使用什么对象类型:JContainerJObjectJToken!只是为了让您知道我想要做什么,一旦正确分配了上述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。这是正确的方法吗?

JTokenJObject 对象似乎可以很好地与LINQ 配合使用时,我不清楚何时/为什么会使用JContainerJContainer的目的是什么?

【问题讨论】:

    标签: c# json linq json.net


    【解决方案1】:

    在大多数情况下,您真的不需要担心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 属性以确定它是什么类型的令牌并适当地进行转换。

    【讨论】:

    • 这个答案提供的信息最丰富,我到处都看过它们之间的关系,并且砰!谢谢你,真的很感谢一个彻底的花絮。
    • 非常好。这是很棒的文档。谢谢兄弟
    • 是的,这很棒。谢谢!
    • 打印出来放在我的办公室。谢谢兄弟!
    • 这是一个很棒的文档。非常感谢。
    【解决方案2】:

    JContainer 是具有子项的 JSON 元素的基类。 JObjectJArrayJPropertyJConstructor 都继承自它。

    例如下面的代码:

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

    会抛出一个InvalidCastException,但如果你把它转换成JContainer,那就没问题了。

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

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

    【讨论】:

    • 在您的第一个示例中,也使用 (JToken)JsonConvert.DeserializeObject("[1, 2, 3]") 有效。我从其他答案中看到,JContainer 可以保存 JToken 可以保存的所有内容,包括其他 JToken。这是唯一的区别吗?您的第二个示例有效,谢谢!
    • JToken 是所有 JSON 元素的基类。您应该只对字符串中期望的元素类型使用 Parse 方法。如果您不知道它是什么,请使用 JToken,然后您将能够将其向下转换为 JObject、JArray 等。在这种情况下,您总是期望 JObject,所以使用它。仅当您想枚举子项而不关心父项是什么时,使用 JContainer 才有用。
    • @EliArbel,这个例子是什么?
    【解决方案3】:

    大多数示例都有简单的 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);
    

    【讨论】:

      猜你喜欢
      • 2020-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      • 2014-07-26
      相关资源
      最近更新 更多