Получение каждого отдельного свойства произвольной формы и его значения, преобразование его в необработанные, читаемые полезные данные JSON
Скажем, я хочу вывести структуру данных JSON заказчику. Эти данные JSON будут:
- Иметь произвольную форму (в основном это может быть значение, объект или даже массив)
- Быть возвращенным во внешний интерфейс напрямую без сериализации (Frontend - это стек JS, возвращающий полезную нагрузку JSON без сериализации строк, что позволило бы ему явным образом выполнить приведение самостоятельно).
Я искал Stackru и нашел хороший фрагмент от @dbc:
/// <summary>
/// Adapted from dbc answer https://stackru.com/a/60997251/3744182
/// To https://stackru.com/questions/60994574/how-to-extract-all-values-for-all-jsonproperty-objects-with-a-specified-name-fro
///
/// </summary>
public static class JsonExtensions
{
public static IEnumerable<JsonElement> DescendantPropertyValues(this JsonElement element)
{
var query = RecursiveEnumerableExtensions.Traverse(
(Name: (string) null, Value: element),
t =>
{
switch (t.Value.ValueKind)
{
case JsonValueKind.Array:
return t.Value.EnumerateArray().Select(i => ((string) null, i));
case JsonValueKind.Object:
return t.Value.EnumerateObject().Select(p => (p.Name, p.Value));
default:
return Enumerable.Empty<(string, JsonElement)>();
}
}, false)
.Select(t => t.Value);
return query;
}
}
public static class RecursiveEnumerableExtensions
{
// Rewritten from the answer by Eric Lippert https://stackru.com/users/88656/eric-lippert
// to "Efficient graph traversal with LINQ - eliminating recursion" https://stackru.com/questions/10253161/efficient-graph-traversal-with-linq-eliminating-recursion
// to ensure items are returned in the order they are encountered.
public static IEnumerable<T> Traverse<T>(
T root,
Func<T, IEnumerable<T>> children, bool includeSelf = true)
{
if (includeSelf)
yield return root;
var stack = new Stack<IEnumerator<T>>();
try
{
stack.Push(children(root).GetEnumerator());
while (stack.Count != 0)
{
var enumerator = stack.Peek();
if (!enumerator.MoveNext())
{
stack.Pop();
enumerator.Dispose();
}
else
{
yield return enumerator.Current;
stack.Push(children(enumerator.Current).GetEnumerator());
}
}
}
finally
{
foreach (var enumerator in stack)
enumerator.Dispose();
}
}
}
С помощью этих методов я мог легко вывести любую структуру данных запрашивающей стороне. Но проблема в том, что значения, возвращаемые в каждом свойстве, просто содержатValueKind
.
Итак, как нам вместо возврата ValueKind вернуть фактическое свойство (будь то объект, массив и т. Д.)?
Еще проще, если существует метод, который имитирует то, что Serialize()
есть, но без включений и всего остального, это будет идеально для внешнего интерфейса, потому что в этом сценарии отсутствует требование внешнего интерфейса для Parse()
входящая сериализованная строка, позволяющая ей "преобразовать" самостоятельно.
Текущий поток кода: данные поступают, я настраиваю ICollection с именемconcatPayload
(Мне нужно, чтобы это была коллекция, потому что мне нужно обрабатывать другие наборы данных / данные, если поступает больше, а иногда это может быть или не быть JSON. Но если это не JSON, он будет систематически преобразован.)
var jsonDoc = JsonDocument.Parse(args.Data);
concatPayload.Add(jsonDoc.RootElement.DescendantPropertyValues()
.Where(v => v.ValueKind.IsPrimitive())
.Select(v => v.Clone()).ToList());
Я выполняю код, рекомендованный dbc выше.
return Ok(await _api.Dispatch(vm)); // The Dispatch method runs the concatPayload population flow.
Проще говоря,
Я беру источник,
Я обрабатываю его, проверяю, JSON это или нет. Если это так, я сделаю вышеупомянутый синтаксический анализ для проверки. Если нет, я конвертирую его в JSON.
Я прячу его как коллекцию
Я возвращаю всю ViewModel
открытый класс DispatchViewModel { /// /// Если это всплывает, пользователь, должно быть, сделал что-то плохое /// публичная строка? ErrorReason {получить; задавать; }
/// <summary> /// The raw payload of the object, in JSON. /// </summary> public ICollection<object> Payload { get; set; } }