我有一个JSON对象。我想确定它是否仅包含特定密钥。这是代码:
{
"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()
方法中到底应该包含什么?有更好的方法吗?谢谢。
注意:这不是以下问题的重复:
How to determine if a Javascript object has only one specific key-value pair? (我的问题是关于C#,而不是JavaScript)。
Determining whether a JSON object contains a specific fieldname in jQuery(我的问题是关于C#的问题,而不是关于jQuery的问题,这不是关于对象中是否存在“字段名”的问题。这是关于键是否为 only 一个对象中存在的一个)。
答案 0 :(得分:3)
您总是可以使用JContainer.Count
来统计JObject
的属性。您想要具有一个名为"Name"
的属性的对象:
foreach (var category in jObject["Categories"].OfType<JObject>())
{
var result = category.Count == 1 && category.Property("Name") != null;
}
您正在使用dynamic
的事实使访问类别JObject
的c#属性变得更加困难,因为可能还会有一个名为"Count"
的JSON属性。我建议改用显式类型的对象和方法。如 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
键组成。
Except(...).Any()
将测试除Name
之外是否还有其他键,但是由于它会被过滤掉,因此不会测试Name
本身是否存在。因此,空的JSON对象{}
将被错误地接受。
相反,您需要检查序列是否相等,例如像这样:
foreach (var category in jObject["Categories"].OfType<JObject>())
{
var result = category.Properties()
.Select(p => p.Name)
.SequenceEqual(new [] { "Name" });
}
如果您需要多个键,那么根据standard,由于JSON对象是名称/值对的无序集合,因此可以对它们进行排序,以要求无序序列相等:
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。
答案 1 :(得分:1)
不使用动力学的替代解决方案是创建与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
是唯一具有值的属性,则此属性返回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();
答案 2 :(得分:0)
一个简单的Where
可以做到:
var categoriesWithMoreThanJustName = JObject.Parse(json)["Categories"]
.Where(category => category.Values<JProperty>().Any(property => property.Name != "Name"));