代码之家  ›  专栏  ›  技术社区  ›  Michael Haddad

如何确定一个JSON对象是否只包含一个特定的键?

  •  1
  • Michael Haddad  · 技术社区  · 6 年前

    {
        "Name": "ThirdParty",
        "Categories": [
            { "Name": "Identity" },
            {
                "Name": "Contact Information",
                "Mandatory": true,
                "Fields": [
                    { "Name": "Phones" },
                    { "Name": "Faxes" },
                    { "Name": "Emails" }
                ]
            },
            { "Name": "Addresses" },
            { "Name": "Bank Accounts" }
        ]
    }
    

    我想确定每个类别是否包含 Name this answer ,我认为解决方案应该是这样的:

    foreach(dynamic category in jObject.Categories)
    {
        var result = category.Children().Except(/* What goes here? */).Any();
    }
    

    但到底该怎么办呢 Except() 方法?有没有更好的办法?谢谢。


    注: 这不是下列问题的重复:

    1. How to determine if a Javascript object has only one specific key-value pair? (我的问题是关于C#,而不是JavaScript)。

    2. Determining whether a JSON object contains a specific fieldname in jQuery (我的问题是关于C#,而不是jQuery,也不是关于对象中是否存在“fieldname”。关键是钥匙是否是钥匙 只有 存在于物体中的物体)。

    3 回复  |  直到 6 年前
        1
  •  3
  •   dbc    6 年前

    你总是可以只计算一个 JObject JContainer.Count . 您需要具有一个属性的对象,名为 "Name" :

    foreach (var category in jObject["Categories"].OfType<JObject>())
    {
        var result = category.Count == 1 && category.Property("Name") != null;
    }
    

    dynamic 使您更难访问类别的c属性 作业对象 ,因为可能还有一个名为 "Count" . 我建议改用显式类型化的对象和方法。这样做还可以进行编译时错误检查,并可能提高性能,如中所述 How does having a dynamic variable affect performance?

    样品提琴 here .

    我应该去看电影吗 Except(...).Any()

    根据 documentation , Enumerable.Except

    设置差异 两个序列。

    所以你可以试试这样:

        var result = !category.Properties()
            .Select(p => p.Name)
            .Except(new [] { "Name" })
            .Any();
    

    但是,这种方法有一个问题:使用 Except()

    ... 确定每个类别是否 包括 Name 仅按键

    除了(…)。任何() 将测试是否有除 ,但它不会测试 密钥本身,因为它将被过滤掉。因此一个空的JSON对象 {} 会被错误地接受。

    相反,您需要检查序列相等性,例如:

    foreach (var category in jObject["Categories"].OfType<JObject>())
    {
        var result = category.Properties()
            .Select(p => p.Name)
            .SequenceEqual(new [] { "Name" });
    }
    

    如果您需要多个键,因为JSON对象是 无序的名称/值对集 standard ,您可以对它们进行排序以要求无序的序列相等:

    var requiredKeys = new [] { "Name" } // Add additional required keys here
        .OrderBy(n => n, StringComparer.Ordinal).ToArray();
    foreach (var category in jObject["Categories"].OfType<JObject>())
    {
        var result = category.Properties()
            .Select(p => p.Name)
            .OrderBy(n => n, StringComparer.Ordinal)
            .SequenceEqual(requiredKeys);
    }
    

    或者如果你愿意你可以用 requiredKeys.ToHashSet(StringComparer.Ordinal) 然后 SetEquals()

    但是,在我看来,如果您只需要检查一个JSON对象是否只包含一个指定的键,那么最初的解决方案是最简单和最具声明性的。

    小提琴演示2 here .

        2
  •  1
  •   Rui Jarimba    6 年前

    不使用dynamics的另一种解决方案是创建与JSON结构匹配的C#模型,然后使用LINQ过滤所需的类别。

    模型:

    public class Data
    {
        public string Name { get; set; }
        public List<Category> Categories { get; set; }
    }
    
    public class Category
    {
        [JsonIgnore]
        public bool HasNameOnly
        {
            get
            {
                return !string.IsNullOrEmpty(Name)
                       && !Mandatory.HasValue
                       && (Fields == null || !Fields.Any());
            }
        }
    
        public string Name { get; set; }
        public bool? Mandatory { get; set; }
        public List<Field> Fields { get; set; }
    }
    
    public class Field
    {
        public string Name { get; set; }
    }
    

    请注意,我添加了 Category.HasNameOnly true if属性 Name 是唯一具有值的属性。

    测试代码:

    string json = @"{
        ""Name"": ""ThirdParty"",
        ""Categories"": [
            { ""Name"": ""Identity"" },
            {
                ""Name"": ""Contact Information"",
                ""Mandatory"": true,
                ""Fields"": [
                    { ""Name"": ""Phones"" },
                    { ""Name"": ""Faxes"" },
                    { ""Name"": ""Emails"" }
                ]
            },
            { ""Name"": ""Addresses"" },
            { ""Name"": ""Bank Accounts"" }
        ]
    }";
    
    Data data = JsonConvert.DeserializeObject<Data>(json);
    
    List<Category> categories = data.Categories.Where(x => x.HasNameOnly).ToList();
    
        3
  •  0
  •   Xiaoy312    6 年前

    Where 将做:

    var categoriesWithMoreThanJustName = JObject.Parse(json)["Categories"]
        .Where(category => category.Values<JProperty>().Any(property => property.Name != "Name"));