JSON.NET. 从其子元素导航到JArray对象

4

有一个 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种类型使用类似的数据结构。这些集合(男性、女性)可以在任何嵌套级别上,例如员工包括男性和女性集合,或学生包括男性和女性集合。我不能为我的解决方案中的每种类型创建强类型对象。有时我需要从men/women集合中删除对象,并检查此后集合是否还有任何元素。

为什么不使用强类型对象? - Yuval Itzchakov
我有大约100种使用类似数据结构的类型。这些集合(男人,女人)可以在任何嵌套级别上,例如员工包括男人和女人集合,或者学生包括男人和女人集合。我不能为解决方案中的每个类型创建强类型对象。有时我需要从男人/女人集合中删除对象并检查此操作后集合是否还有任何元素。 - Maksym Slavnenko
2个回答

2

这不是一个直接回答“如何使用LINQ to Json查询”的问题,而是一种我发现更容易使用的强类型对象方法。

只需创建以下数据结构(请注意POCO使用了Json.NET属性JsonProperty):

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

1
你的代码不起作用是因为你正在操作所有值以 "J" 开头的 name 属性,而不是包含值以 "J" 开头的 name 属性的人对象。所以当你向上导航时,你还没有走够。 name 属性的父级是人对象。人对象的父级是数组,数组的父级是 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"));
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接