LINQ to JSON - запрос массива

Мне нужно выбрать пользователей, которые имеют "3" в своем массиве json.

{
    "People":[  
    {  
        "id" : "123",
        "firstName" : "Bill",
        "lastName" : "Gates",
        "roleIds" : {
                "int" : ["3", "9", "1"]
            }
    },
    {  
        "id" : "456",
        "firstName" : "Steve",
        "lastName" : "Jobs",
        "roleIds" : {
            "int" : ["3", "1"]
        }
    },
    {  
        "id" : "789",
        "firstName" : "Elon",
        "lastName" : "Musk",
        "roleIds" : {
            "int" : ["3", "7"]
        }
    },
    {
        "id" : "012",
        "firstName" : "Agatha",
        "lastName" : "Christie",
        "roleIds" : {
            "int" : "2"
        }
    }
]}

В конце концов, мои результаты должны быть Элон Маск и Стив Джобс. Это код, который я использовал (и другие варианты):

var roleIds = pplFeed["People"]["roleIds"].Children()["int"].Values<string>();


var resAnAssocInfo = pplFeed["People"]
.Where(p => p["roleIds"].Children()["int"].Values<string>().Contains("3"))
.Select(p => new
{
    id = p["id"],
    FName = p["firstName"],
    LName = p["lastName"]
}).ToList();

Я получаю следующую ошибку:

"Accessed JArray values with invalid key value: "roleIds".  Int32 array index expected"

Я изменился .Values<string>() в .Values<int>() и до сих пор не повезло.

Что я делаю неправильно?

2 ответа

Решение

Вы довольно близко Измени свой Where пункт из этого:

.Where(p => p["roleIds"].Children()["int"].Values<string>().Contains("3"))

к этому:

.Where(p => p["roleIds"]["int"].Children().Contains("3"))

и вы получите желаемый результат (хотя в ваших примерах данных фактически есть три пользователя с идентификатором роли "3"а не два).

Однако есть еще одна проблема, с которой вы можете столкнуться, и этот код все равно не будет работать. Вы заметите, что для Агаты Кристи ценность int это не массив, как другие, это простая строка. Если значение иногда будет массивом, а иногда нет, тогда вам нужно предложение where, которое может обрабатывать оба. Примерно так должно работать:

.Where(p => p["roleIds"]["int"].Children().Contains(roleId) ||
            p["roleIds"]["int"].ToString() == roleId)

...где roleId это строка, содержащая идентификатор, который вы ищете.

Скрипка: https://dotnetfiddle.net/Zr1b6R

Проблема в том, что не все объекты используют один и тот же интерфейс. Последний элемент в этом списке имеет одно строковое значение в roleIds.int свойство, в то время как все остальные имеют массив. Вам необходимо нормализовать это свойство, а затем выполнить проверку. Будет проще, если они все будут массивами.

Вы должны быть в состоянии сделать это:

var roleId = "3";
var query =
    from p in pplFeed["People"]
    let roleIds = p.SelectToken("roleIds.int")
    let normalized = roleIds.Type == JTokenType.Array ? roleIds : new JArray(roleIds)
    where normalized.Values().Contains(roleId)
    select new
    {
        id = p["id"],
        FName = p["firstName"],
        LName = p["lastName"],
    };
Другие вопросы по тегам