Найти значение свойства из вложенного JSON без рекурсии в C# - PullRequest
0 голосов
/ 24 апреля 2020

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

    public static class JsonExtensions
    {
        /// <summary>
        /// Finds the value of a JToken from the given JSON data for the given property name.
        /// If the resultant JToken have multiple occurrence at different levels of JSON data the this methods returns the first or default.
        /// </summary>
        /// <param name="containerToken">Given JSON data.</param>
        /// <param name="propertyName">Property name whose value needs to be find.</param>
        /// <returns></returns>
        public static string FindJToken(this JToken containerToken, string propertyName)
        {
            if (containerToken == null)
            {
                throw new ArgumentNullException(nameof(containerToken));
            }

            if (string.IsNullOrWhiteSpace(propertyName))
            {
                throw new ArgumentException("Parameter Cannot be Null, Empty or White spaces.", nameof(propertyName));
            }

            List<JToken> matches = new List<JToken>();
            GetJTokenValue(containerToken, propertyName, matches);
            return matches.FirstOrDefault()?.ToString();
        }

        /// <summary>
        /// Recursive method to find value of the given JToken / property name from the given JSON
        /// </summary>
        /// <param name="containerToken">Given JSON data</param>
        /// <param name="propertyName">Property name whose value needs to be find.</param>
        /// <param name="matches">List of matching tokens</param>
        private static void GetJTokenValue(JToken containerToken, string propertyName, List<JToken> matches)
        {
            if (containerToken.Type == JTokenType.Object)
            {
                foreach (JProperty child in containerToken.Children<JProperty>())
                {
                    if (child.Name == propertyName)
                    {
                        matches.Add(child.Value);
                    }
                    GetJTokenValue(child.Value, propertyName, matches);
                }
            }
            else if (containerToken.Type == JTokenType.Array)
            {
                foreach (JToken child in containerToken.Children())
                {
                    GetJTokenValue(child, propertyName, matches);
                }
            }
        }
}

Я хочу преобразовать метод GetJTokenValue () в качестве итератора и изменить совпадения в IEnumerable, чтобы избежать рекурсии и снизить сложность. Любая помощь будет оценена.

Спасибо и С уважением,

Амит Ананд

Ответы [ 2 ]

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

Вы пробовали использовать JsonPath? Я нахожу это очень полезным для подобных ситуаций.

Вы на самом деле не приводили исходный пример, поэтому я просто придумал что-то простое:

//{
//  "ToBeFound": "test",
//  "lala": [
//    {
//     "ToBeFound": "test"
//    },
//    {
//     "NotToBeFound": "test2"
//    }
//  ]
//}

var source = JToken.Parse(@"{ ""ToBeFound"": ""test"", ""lala"": [ { ""ToBeFound"": ""test"" }, { ""NotToBeFound"": ""test2"" } ] }");

var results = source.SelectTokens("$..ToBeFound");

foreach(JToken result in results)
    Console.WriteLine(result.Value<string>());

Посмотрите, как оно выполняется: https://dotnetfiddle.net/zoH7FQ

Надеюсь, это поможет вам!

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

Вместо создания указанного c метода для поиска имени, вы можете создать более общую c версию для ленивого перебора всех свойств-потомков, используя yield.

public static IEnumerable<JProperty> GetDescendantProperties(this JToken containerToken)
{
    var children = containerToken.Children();

    foreach (var child in children)
    {
        if (child is JProperty property)
        {
            yield return property;
        }
        var descendants = child.GetDescendantProperties();
        foreach (var descendant in descendants)
        {
            yield return descendant;
        }
    }
}

Теперь Вы можете найти свой первый матч:

var match = myToken.GetDescendants().FirstOrDefault(token => token.Name == propertyName);

Или все совпадения:

var matches = myToken.GetDescendants().Where(token => token.Name == propertyName).ToList();
...