En una pregunta mía anterior, pregunté cómo llenar un objeto existente usando System.Text.Json.
Una de las mejores respuestas mostró una solución que analizaba la cadena json con JsonDocument y la enumeraba con EnumerateObject .
Con el tiempo, mi cadena json evolucionó y ahora también contiene una matriz de objetos, y al analizar eso con el código de la respuesta vinculada, arroja la siguiente excepción:
The requested operation requires an element of type 'Object', but the target element has type 'Array'. Descubrí que uno puede, de una forma u otra, buscar JsonValueKind.Array y hacer algo como esto
if (json.ValueKind.Equals(JsonValueKind.Array)) { foreach (var item in json.EnumerateArray()) { foreach (var property in item.EnumerateObject()) { await OverwriteProperty(???); } } }pero no puedo hacer que funcione.
¿Cómo hacer esto, y como una solución genérica?
Me gustaría obtener el "Resultado 1" , donde se agregan/actualizan los elementos de la matriz, y el "Resultado 2" (al pasar una variable), donde se reemplaza toda la matriz.
Para el "Resultado 2" , supongo que uno puede detectar if (JsonValueKind.Array)) en el método OverwriteProperty , y ¿dónde/cómo pasar la variable "replaceArray"? ... mientras itera la matriz o los objetos?
Algunos datos de muestra:
Cadena Json inicial
{ "Title": "Startpage", "Links": [ { "Id": 10, "Text": "Start", "Link": "/index" }, { "Id": 11, "Text": "Info", "Link": "/info" } ] }Cadena Json para agregar/actualizar
{ "Head": "Latest news", "Links": [ { "Id": 11, "Text": "News", "Link": "/news" }, { "Id": 21, "Text": "More News", "Link": "/morenews" } ] }resultado 1
{ "Title": "Startpage", "Head": "Latest news" "Links": [ { "Id": 10, "Text": "Start", "Link": "/indexnews" }, { "Id": 11, "Text": "News", "Link": "/news" }, { "Id": 21, "Text": "More news", "Link": "/morenews" } ] }resultado 2
{ "Title": "Startpage", "Head": "Latest news" "Links": [ { "Id": 11, "Text": "News", "Link": "/news" }, { "Id": 21, "Text": "More News", "Link": "/morenews" } ] }Clases
public class Pages { public string Title { get; set; } public string Head { get; set; } public List<Links> Links { get; set; } } public class Links { public int Id { get; set; } public string Text { get; set; } public string Link { get; set; } }Código C#:
public async Task PopulateObjectAsync(object target, string source, Type type, bool replaceArrays = false) { using var json = JsonDocument.Parse(source).RootElement; if (json.ValueKind.Equals(JsonValueKind.Array)) { foreach (var item in json.EnumerateArray()) { foreach (var property in item.EnumerateObject()) { await OverwriteProperty(???, replaceArray); //use "replaceArray" here ? } } } else { foreach (var property in json.EnumerateObject()) { await OverwriteProperty(target, property, type, replaceArray); //use "replaceArray" here ? } } return; } public async Task OverwriteProperty(object target, JsonProperty updatedProperty, Type type, bool replaceArrays) { var propertyInfo = type.GetProperty(updatedProperty.Name); if (propertyInfo == null) { return; } var propertyType = propertyInfo.PropertyType; object parsedValue; if (propertyType.IsValueType) { parsedValue = JsonSerializer.Deserialize( updatedProperty.Value.GetRawText(), propertyType); } else if (replaceArrays && "property is JsonValueKind.Array") //pseudo code sample { // use same code here as in above "IsValueType" ? } else { parsedValue = propertyInfo.GetValue(target); await PopulateObjectAsync( parsedValue, updatedProperty.Value.GetRawText(), propertyType); } propertyInfo.SetValue(target, parsedValue); }Bueno, si no te importa cómo se escriben las matrices, tengo una solución simple. Cree un nuevo JSON dentro de 2 fases 1 bucle para nuevas propiedades y 1 bucle para las actualizaciones:
var sourceJson = @" { ""Title"": ""Startpage"", ""Links"": [ { ""Id"": 10, ""Text"": ""Start"", ""Link"": ""/index"" }, { ""Id"": 11, ""Text"": ""Info"", ""Link"": ""/info"" } ] }"; var updateJson = @" { ""Head"": ""Latest news"", ""Links"": [ { ""Id"": 11, ""Text"": ""News"", ""Link"": ""/news"" }, { ""Id"": 21, ""Text"": ""More News"", ""Link"": ""/morenews"" } ] } "; using var source = JsonDocument.Parse(sourceJson); using var update = JsonDocument.Parse(updateJson); using var stream = new MemoryStream(); using var writer = new Utf8JsonWriter(stream); writer.WriteStartObject(); // write non existing properties foreach (var prop in update.RootElement.EnumerateObject().Where(prop => !source.RootElement.TryGetProperty(prop.Name, out _))) { prop.WriteTo(writer); } // make updates for existing foreach (var prop in source.RootElement.EnumerateObject()) { if (update.RootElement.TryGetProperty(prop.Name, out var overwrite)) { writer.WritePropertyName(prop.Name); overwrite.WriteTo(writer); } else { prop.WriteTo(writer); } } writer.WriteEndObject(); writer.Flush(); var resultJson = Encoding.UTF8.GetString(stream.ToArray()); Console.WriteLine(resultJson);Producción :
{ "Head":"Latest news", "Title":"Startpage", "Links":[ { "Id":11, "Text":"News", "Link":"/news" }, { "Id":21, "Text":"More News", "Link":"/morenews" } ] }