У меня есть произвольно определенный документ JSON, и я хочу иметь возможность применить выражение JSONPath в виде белого списка фильтр для свойств: все выбранные узлы и их предки до корневого узла остаются, все остальные узлы удаляются. Если узлов не существует, я должен получить пустой документ.
Кажется, что в JSON.Net не было ничего похожего на это, и я нигде не мог найти подобных примеров, поэтому я создал свой собственный. Я решил скопировать выбранные узлы во вновь созданный документ, а не пытаться удалить все узлы, которые не совпадают. Учитывая, что может быть несколько совпадений, а документы могут быть большими, необходимо иметь возможность эффективно обрабатывать объединение результатов множественного выбора в единый документ дерева / JSON.
Моя попытка вроде работает, но я получаю странные результаты. Процесс включает MergedAncestry
метод, который выполняет итерацию по SelectTokens
результатам, вызывает GetFullAncestry
(который рекурсивно строит дерево до этого узла), а затем объединяет результаты. Однако похоже, что слияние JArrays происходит на неправильном уровне, как вы можете видеть в разделе «Фактические результаты» ниже.
Мои вопросы:
- Есть ли лучший / более быстрый / встроенный способ добиться этого?
- Если нет, то что я делаю не так?
Код:
public static void Main()
{
string json = @"..."; // snipped for brevity - see DotNetFiddle: https://dotnetfiddle.net/wKN1Hj
var root = (JContainer)JToken.Parse(json);
var t3 = root.SelectTokens("$.Array3B.[*].Array3B1.[*].*");
// See DotNetFiddle for simpler examples that work
Console.WriteLine($"{MergedAncestry(t3).ToString()}"); // Wrong output!
Console.ReadKey();
}
// Returns a single document merged using the full ancestry of each of the input tokens
static JToken MergedAncestry(IEnumerable<JToken> tokens)
{
JObject merged = null;
foreach(var token in tokens)
{
if (merged == null)
{
// First object
merged = (JObject)GetFullAncestry(token);
}
else
{
// Subsequent objects merged
merged.Merge((JObject)GetFullAncestry(token), new JsonMergeSettings
{
// union array values together to avoid duplicates
MergeArrayHandling = MergeArrayHandling.Union
});
}
}
return merged ?? new JObject();
}
// Recursively builds a new tree to the node matching the ancestry of the original node
static JToken GetFullAncestry(JToken node, JToken tree = null)
{
if (tree == null)
{
// First level: start by cloning the current node
tree = node?.DeepClone();
}
if (node?.Parent == null)
{
// No parents left, return the tree we've built
return tree;
}
// Rebuild the parent node in our tree based on the type of node
JToken a;
switch (node.Parent)
{
case JArray _:
return GetFullAncestry(node.Parent, new JArray(tree));
case JProperty _:
return GetFullAncestry(node.Parent, new JProperty(((JProperty)node.Parent).Name, tree));
case JObject _:
return GetFullAncestry(node.Parent, new JObject(tree));
default:
return tree;
}
}
Пример JSON:
{
"Array3A": [
{ "Item_3A1": "Desc_3A1" }
],
"Array3B": [
{ "Item_3B1": "Desc_3B1" },
{
"Array3B1": [
{ "Item_1": "Desc_3B11" },
{ "Item_2": "Desc_3B12" },
{ "Item_3": "Desc_3B13" }
]
},
{
"Array3B2": [
{ "Item_1": "Desc_3B21" },
{ "Item_2": "Desc_3B22" },
{ "Item_3": "Desc_3B23" }
]
}
]
}
Полный код и тесты см. В DotNetFiddle.
"Фильтр" JSONPath:
$.Array3B.[*].Array3B1.[*].*
Ожидаемые результаты:
{
"Array3B": [
{
"Array3B1": [
{ "Item_1": "Desc_3B11" },
{ "Item_2": "Desc_3B12" },
{ "Item_3": "Desc_3B13" }
]
}
]
}
Фактические результаты:
{
"Array3B": [
{
"Array3B1": [ { "Item_1": "Desc_3B11" } ]
},
{
"Array3B1": [ { "Item_2": "Desc_3B12" } ]
},
{
"Array3B1": [ { "Item_3": "Desc_3B13" } ]
}
]
}
JsonExtensions.RemoveAllExcept<TJToken>(this TJToken obj, IEnumerable<string> paths)
из этого ответа на Как выполнить частичную сериализацию объекта с указанием« путей »с помощью Newtonsoft JSON.NET? - person dbc   schedule 17.08.2019RemoveAllExcept
, когда входные строки JSONPath соответствуют всем узлам, примерно на 50-100% быстрее, когда он соответствует половине узлов и на порядок быстрее при небольшом количестве совпадений (документ 8 МБ, 100 итераций). Не уничтожать оригинал будет полезно для моих целей, поэтому я буду придерживаться своего ответа, но я буду рад проголосовать за, если вы добавите свой ответ. Еще раз спасибо за ваш вклад! - person pcdev   schedule 19.08.2019