JContainer, JObject, JToken and Linq confusion



我很难理解何时使用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"];

然后在jsonObject1中得到一个JObject类型。这是正确的方法吗?

JTokenJObject对象与LINQ配合得很好时,我不清楚何时/为什么要使用JContainerJContainer的用途是什么?

在大多数情况下,您实际上不需要担心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)

因此,您可以看到,JObjectJContainer,而则是JToken

以下是基本经验法则:

  • 如果您知道您有一个对象(在JSON中用大括号{}表示),请使用JObject
  • 如果您知道您有一个数组或列表(用方括号[]表示),请使用JArray
  • 如果您知道您有一个基元值,请使用JValue
  • 如果您不知道自己拥有哪种令牌,或者希望能够以通用方式处理以上任何一种令牌,请使用JToken。然后,您可以检查其Type属性,以确定它是什么类型的令牌,并对其进行适当的强制转换

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

例如,以下代码:

(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);

相关内容

  • 没有找到相关文章

最新更新