JSON.NET。从其子项导航到 JArray 对象

JSON.NET. Navigate to JArray object from its children

有一个这样的 JSON 文件:

{
  "men": [
    {
      "name": "Jordan",
      "phone": "333-333-33"
    },
    {
      "name": "Timothey",
      "phone": "444-444-44"
    }
  ],
  "women": [
    {
      "name": "Jordan",
      "phone": "111-111-11"
    },
    {
      "name": "Sasha",
      "phone": "222-222-22"
    }
  ]
}

我想找出所有名字以J开头的人,并判断这个人是男是女。

var jsonProps = jsonDoc.Descendants().Where(t => t.Type == JTokenType.Property && ((JProperty)t).Name == prop);
var startsWithJ = jsonProps.Where(t => ((JProperty)t).Value.ToString().StartsWith("J"));

foreach (var person in startsWithJ)
{
    Console.WriteLine(person.Value<string>());
    Console.WriteLine(person.Parent.Parent.Value<string>());
}

问题是 person.Parent.Parent 为 null,我希望它不是 JArray,或者至少是 JToken,或者至少是一些 JToken,这样我才能得到它的值。

更新: 我有 100 种使用相似数据结构的类型。这些集合(男性、女性)可以位于任何嵌套级别,例如employee包含男女集合,或者student包含男女集合。我无法为解决方案中的每种类型创建强类型对象。有时我需要从 men/women 集合中移除对象并检查集合是否有此之后的任何元素。

这不是对 "how you query with LINQ to Json" 的直接回答,而是我发现使用强类型对象更容易的方法。

通过简单地创建以下数据结构(注意 POCO 用 JsonProperty 修饰,这是一个 Json.NET 属性):

public class RootObject
{
    public List<Person> Men { get; set; }
    public List<Person> Women { get; set; }
}

public class Person
{
    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("phone")]
    public string PhoneNumber { get; set; }

    public Sex Sex { get; set }
}

public enum Sex
{
    Man,
    Women
}

现在,您可以使用 LINQ to Objects 轻松查询所需内容:

var rootObject = JsonConvert.DeserializeObject<RootObject>(json);

foreach (var male in rootObject.Men)
{
    male.Sex = Sex.Man;
}

foreach (var female in rootObject.Women)
{
    female.Sex = Sex.Women;
}

var startsWithJ = rootObject.Men.Concat(rootObject.Women)
                            .Where(x => x.Name.StartsWith("J",
                                        StringComparison.OrdinalIgnoreCase))
                            .ToList();

foreach (var personStartsWithJ in startsWithJ)
{
    Console.WriteLine (personStartsWithJ.Name);
    Console.WriteLine (personStartsWithJ.Sex);
}

您的代码不起作用,因为您操作的是值以 "J" 开头的所有 name 属性,而不是包含 name 属性的 person 对象具有以 "J" 开头的值。所以当你向上导航时,你走的还不够远。 name 属性 的父对象是 person 对象。 person对象的父对象是数组,数组的父对象是men(或womenJProperty。 (此外,要确定包含 属性 的数组的名称是 "men" 还是 "women",您需要获取它的 Name,而不是它的 Value.)

因此,如果您将代码更改为以下内容,您应该会得到预期的结果:

foreach (JProperty nameProp in startsWithJ)
{
    Console.WriteLine(nameProp.Value);
    Console.WriteLine(((JProperty)nameProp.Parent.Parent.Parent).Name);
}

Fiddle: https://dotnetfiddle.net/J7WxiF

就我个人而言,我发现 JObject 级别比 JProperty 级别更容易工作。这是获得相同结果的另一种方法:

var people = jsonDoc["men"]
    .Children<JObject>()
    .Union(jsonDoc["women"].Children<JObject>())
    .Where(obj => obj["name"] != null && obj["name"].ToString().StartsWith("J"));

foreach (JObject person in people)
{
    Console.WriteLine(person["name"]);
    Console.WriteLine(((JProperty)person.Parent.Parent).Name);
}

Fiddle: https://dotnetfiddle.net/jzUOFT

如果你发现你总是想通过这种方式找到这个人的父集合名称,你甚至可以定义一个扩展方法:

public static class JHelper
{
    public static bool IsMale(this JObject person)
    {
        return ((JProperty)person.Parent.Parent).Name == "men";
    }
}

然后您可以简单地在 JObject 上调用 IsMale()。它使代码更具可读性。

foreach (JObject person in people)
{
    Console.WriteLine("Name: " + person["name"]);
    Console.WriteLine("Gender: " + (person.IsMale() ? "male" : "female"));
}