Как определить, содержит ли объект JSON только определенный ключ? - PullRequest
0 голосов
/ 13 октября 2018

У меня есть объект JSON.Я хочу определить, содержит ли он только определенный ключ.Вот код:

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

Я хочу определить для каждой категории, состоит ли она только из клавиши Name.Используя этот ответ , я считаю, что решение должно выглядеть примерно так:

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

Но что именно должно пойти в методе Except()?Есть ли лучший способ сделать это?Спасибо.


Примечание: Это не дубликат следующих вопросов:

  1. Как определить, является ли Javascriptобъект имеет только одну конкретную пару ключ-значение? (мой вопрос о C #, а не о JavaScript).

  2. Определение, содержит ли объект JSON конкретное имя поля в jQuery (мой вопрос касается C #, а не jQuery, и дело не в том, существует ли «имя поля» в объекте. Вопрос в том, является ли ключ only существующим в объекте).

Ответы [ 3 ]

0 голосов
/ 14 октября 2018

Вы всегда можете просто посчитать свойства JObject, используя JContainer.Count.Вам нужны объекты с одним свойством с именем "Name":

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

Тот факт, что вы используете dynamic, затрудняет доступ к свойствам c # вашей категории JObject, посколькутакже может быть свойством JSON с именем "Count".Я бы предложил вместо этого перейти к явно типизированным объектам и методам.Это также даст вам проверку ошибок во время компиляции и, возможно, улучшенную производительность, как объяснено в Как влияет динамическая переменная на производительность? .

Примерfiddle здесь .

Обновление

Если я пойду на решение Except(...).Any(), что мне нужно будет поставить в скобках?

Согласно документации , 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" });
}

И если вам требуется присутствиеиз нескольких ключей, так как объект 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 здесь .

0 голосов
/ 14 октября 2018

Альтернативное решение без использования динамики - это создание моделей C #, соответствующих вашей структуре JSON, а затем использование 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();
0 голосов
/ 14 октября 2018

Простой Where сделает:

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