Комплекс фильтрации JSON по Dynami c фильтр запросов в c# - PullRequest
1 голос
/ 21 апреля 2020

Я пытаюсь создать JSON фильтрацию в одном из моих проектов, и здесь JSON будет динамическим c, поэтому не могу создать модель, и фильтр будет получен от пользователя, мой образец JSON равен

[
  {
    "id": 101,
    "field1": "f1",
    "field2": "f2",
    "InnerArray": [
      {
        "id": 201,
        "innerField1": "f1",
        "innerField2": "f2"
      },
      {
        "id": 202,
        "innerField1": "f1",
        "innerField2": "f2"
      }
    ]
  },
  {
    "id": 102,
    "field1": "ff1",
    "field2": "ff2",
    "InnerArray": [
      {
        "id": 301,
        "innerField1": "f1",
        "innerField2": "f2"
      },
      {
        "id": 302,
        "innerField1": "f1",
        "innerField2": "f2"
      }
    ]
  }
]

Я пытаюсь отфильтровать это по SelectToken (), и это будет работать нормально, за исключением внутреннего массива, например, если запрос

string filter = "$.[?(@.id==101)]";
JToken filteredData = data.SelectToken($"{filter}");

//We will get
{
  "id": 101,
  "field1": "f1",
  "field2": "f2",
  "InnerArray": [
    {
      "id": 201,
      "innerField1": "f1",
      "innerField2": "f2"
    },
    {
      "id": 202,
      "innerField1": "f1",
      "innerField2": "f2"
    }
  ]
}

, но если я хочу отфильтровать JSOn по внутреннему элементу массива он не будет работать

string filter = "$.[?(@.InnerArray[?(@.id==301)])]";
JToken filteredData = data.SelectToken($"{filter}");

//Result is 
{
  "id": 102,
  "field1": "ff1",
  "field2": "ff2",
  "InnerArray": [
    {
      "id": 301,
      "innerField1": "f1",
      "innerField2": "f2"
    },
    {
      "id": 302,
      "innerField1": "f1",
      "innerField2": "f2"
    }
  ]
}

Я ожидаю

{
  "id": 102,
  "field1": "ff1",
  "field2": "ff2",
  "InnerArray": [
    {
      "id": 301,
      "innerField1": "f1",
      "innerField2": "f2"
    }
  ]
}

Фильтр InnerArray, возвращающий все элементы, а внутренний JSON PATH не принимает, есть ли альтернативный способ определить JSON путь? или есть какая-либо альтернатива для динамической фильтрации JSON, поскольку здесь JSON будет динамически c, а фильтр будет динамически c

Ответы [ 3 ]

1 голос
/ 21 апреля 2020

Возможно, я построил для этого следующий исполняемый код:

Чтобы сделать его анализируемым, в противном случае JToken.Parse говорит, что json не может начинаться как массив.

string sourceFile = File.ReadAllText("./source.json");
JToken source = JToken.Parse(sourceFile);

List<JToken> tokensToRemove = source.SelectTokens("$..*[?(@.id == 101 || @.id == 301)]").ToList();

tokensToRemove.ForEach(t => t.Remove());

string result = source.ToString();

Результат будет содержать то, что вы сказали, что ожидали.

К вашему сведению, $ .. выбирает все элементы из родителя независимо от их глубины.

--- EDIT:

Для последующего вопроса об обратном. Это возможно, но вам придется подойти к этому немного по-другому. Поскольку элементы находятся на разных уровнях исходного объекта, я думаю, что лучше всего создать новый объект JObject с массивом результатов, которые вы хотите получить.

Примерно так:

string sourceFile = File.ReadAllText("./source.json");
JToken source = JToken.Parse(sourceFile);

List<JToken> tokensToKeep = source.SelectTokens("$..*[?(@.id == 101 || @.id == 301)]").ToList();

JObject resultObject = new JObject();
JArray array = new JArray();
resultObject.Add("array", array);
tokensToKeep.ForEach(t => array.Add(t));

string result = resultObject.ToString();
0 голосов
/ 21 апреля 2020

Я думаю, что возможно создать модель. См. Дизайн ниже.

internal class Inner
{
    public int id { get; set; }
    public string innerField1 { get; set; }

    public string innerField2 { get; set; }
}
internal class Outer
{
    public int id { get; set; }
    public string field1 { get; set; }

    public string field2 { get; set; }

    public List<Inner> InnerArray { get; set; }

}

Пример этой модели, как показано ниже

private static void Main(string[] args)
    {
        List<Outer> list = new List<Outer>
        {
            new Outer() 
            { 
                id = 1, field1 = "f1", 
                field2 = "f2", 
                InnerArray = new List<Inner>() 
                { 
                    new Inner() 
                    { 
                        id = 1,
                        innerField1="if1",
                        innerField2="if2" 
                    } 
                } 
            },
            new Outer() 
            { 
                id = 2, 
                field1 = "f1", 
                field2 = "f2", 
                InnerArray = new List<Inner>() 
                { 
                    new Inner() 
                    { 
                        id = 1,
                        innerField1="if1",
                        innerField2="if2" 
                    } 
                } 
            }
        };

        string serializedObject = JsonConvert.SerializeObject(list, Formatting.Indented);

        Console.WriteLine(serializedObject);

        Console.ReadLine();

    }

В serializedObject вы получите эту строку json, снова вы можете десериализовать строку с помощью JsonConvert.DeserializeObject ( ...), а затем вы можете отфильтровать свой объект с помощью Linq.

 var deserializeList = JsonConvert.DeserializeObject<List<Outer>>(serializedObject);

        var outer = deserializeList.FirstOrDefault(x => x.id == 1);

        Console.WriteLine(outer?.id);

Надеюсь, эта помощь.

0 голосов
/ 21 апреля 2020

Я не думаю, что ваше ожидание может быть достигнуто с помощью JSONPath (поправьте меня, если я ошибаюсь).

$[?(@.InnerArray[?(@.id==301)])]

предназначено для выбора токенов из родительского массива с помощью фильтры применяются к InnerArray свойству дочерних объектов.

Таким образом, в Engli sh это означает:

Данный родительский объект, если его InnerArray содержит какой-либо объект, который определяет свойство id, а значение такого свойства id равно 301, затем возвращает родительский объект .

Чтобы выбрать токены из InnerArray, вам следует это сделать (при условии id уникально):

$[?(@.InnerArray[?(@.id==301)])].InnerArray[0]

Результат:

[
   {
      "id":301,
      "innerField1":"f1",
      "innerField2":"f2"
   }
]

Затем необходимо заменить InnerArray в выбранном родительском элементе:

var selectedInnerArray = obj.SelectToken(
    "$[?(@.InnerArray[?(@.id==301)])].InnerArray[0]");
var selectedParent = obj.SelectToken("$[?(@.InnerArray[?(@.id==301)])]");

var result = selectedParent.DeepClone();
result["InnerArray"].Replace(new JArray(selectedInnerArray));

result будет выглядеть как ваше ожидание.

Что касается обобщенной c фильтрации с динамическим c фильтром и JSON структурой, я не думаю, что смогу на это ответить. Вам нужно объединить выбранные токены, чтобы получить ваши ожидания, и я не знаю, как легко определить эти действия слияния.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...