Runtime: Admite DOM "dinámico" y escribible

Creado en 29 may. 2019  ·  47Comentarios  ·  Fuente: dotnet/runtime

¿ JsonSerializer.Parse(String, Type, JsonSerializerOptions) compatible con el tipo de retorno dinámico ExpandoObject ?

Algo como esto:
dynamic p = JsonSerializer.Parse(json, typeof(ExpandoObject));

area-System.Text.Json enhancement json-functionality-doc

Comentario más útil

También tengo un caso de uso para esto: solo quiero llamar a una API REST con HttpClient y recuperar una sola propiedad de la respuesta. No quiero crear una clase dedicada solo para analizar la respuesta ... Con JSON.NET podría usar "dinámico" y acceder a la propiedad de elección.

Todos 47 comentarios

No, esta función no es compatible en este momento, pero es algo que deberíamos considerar para vNext. ¿Tiene un uso de ejemplo para motivar la solicitud de función?

Marcando como futuro.

System.NotSupportedException : The collection type 'System.Dynamic.ExpandoObject' is not supported.

@ahsonkhan GraphQL es un buen ejemplo.

La especificación recomienda JSON pero no está vinculada a ninguna serialización específica en la respuesta.
Esto implica que el campo "datos" de la respuesta es del tipo: dinámico. Como no se puede inferir.
Sin ExpandoObject, la deserialización hace dinámico un tipo de creación JSON. Por lo tanto, el acceso a esos "datos" dinámicos abstractos debe hacerse sabiendo que, de hecho, esa dinámica es un JToken.

Con ExpandoObject creo que podríamos imponer el acceso de los "datos" dinámicos como un objeto común

@ahsonkhan otro ejemplo es el Servicio de configuración en nuestro proyecto. Expone una colección como los puntos finales REST, que crean colecciones en MongoDB (no es solo un contenedor REST ficticio, y las colecciones rest y la colección mongo no tienen un mapeo 1-1 exacto, también afirma ciertas reglas).

Entonces, en nuestro proyecto, necesitamos soporte dinámico / ExpandoObject.

También lo usamos en otros microservicios.

También nos encontramos con esta limitación. Nuestro caso de uso es construir gradualmente un objeto dinámico antes de la serialización json. Volví al serializador Json.NET más maduro.

Hola chicos

¿Cuál es el paseo por ahora?
¿Puedo configurar cuál usar?
@SidShetye dijiste algo sobre volver a uno más maduro, ¿podrías explicarme, por favor?

@MickeyReznikov , ¿se respondió tu pregunta? Creo que @SidShetye significó volver a usar Newtonsoft.Json para serializar objetos dinámicos ya que la biblioteca en caja (System.Text.Json) aún no tiene soporte para esos. En el caso de las aplicaciones asp.net, puede configurarlo para volver a AddNewtonsoftJson.

Consulte https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.0&tabs=visual-studio#jsonnet -support

También tengo un caso de uso para esto: solo quiero llamar a una API REST con HttpClient y recuperar una sola propiedad de la respuesta. No quiero crear una clase dedicada solo para analizar la respuesta ... Con JSON.NET podría usar "dinámico" y acceder a la propiedad de elección.

De https://github.com/dotnet/corefx/issues/41472 por @ ghost1372 :

hola espero que este sea un buen lugar para hacer esta pregunta
Parece que no podemos deserializar objetos dinámicos.
Usé este código pero no funcionó, ¿hay alguna forma de hacer esto?

var objList = System.Text.Json.JsonSerializer.Deserialize<List<dynamic>>(json);

En nuestras muchas aplicaciones, estamos controlando los campos de datos de los procedimientos almacenados y renderizamos dinámicamente las páginas de Lista y Lista de búsqueda con jquery.jtable

Sin el soporte integrado de JsonSerializer para ExpandoObject, no podemos usar la serialización Json integrada de dotnet core

Compartimos el mismo caso de uso que ya han mencionado @estiller y @SidShetye
Mientras tanto, tuvimos que volver a Json.NET.

¿Es posible tener un hito más cercano a _ahora_ que a Futuro ? 🤔

ExpandoObject ha estado en la BCL durante muuucho tiempo

¿Existe alguna alternativa para ExpandoObject?

@fatihyildizhan No, no hay suplente.

pero hemos escrito nuestro propio convertidor ExpandoObject, puede seguir la pista de este artículo ASP.NET Core 3.0: Custom JsonConverter para el nuevo System.Text.Json

Solo necesitamos la serialización, por lo que solo creamos la serialización

Me sorprendió que esto aún no sea compatible.

Moviendo esto a 5.0.

¿Pasando a 5.0? ¿Eso significa que tenemos que esperar al menos un año? De vuelta a JSON.Net lo es.

5,0? wow, eso definitivamente apesta.

Para una solución temporal fea, puedo usar JsonDocument con un convertidor personalizado, pero es IDisposable.

public sealed class EventObject
    {
        [JsonPropertyName("id")]
        public long Id
        {
            get; set;
        }

        [JsonPropertyName("eventData")]
        [JsonConverter(typeof(JsonDocumentConverter))]
        public System.Text.Json.JsonDocument EventData
        {
            get; set;
        }
    }

    internal sealed class JsonDocumentConverter
        : JsonConverter<System.Text.Json.JsonDocument>
    {
        public override JsonDocument Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            return JsonDocument.ParseValue(ref reader);
        }

        public override void Write(Utf8JsonWriter writer, JsonDocument value, JsonSerializerOptions options)
        {
            value.WriteTo(writer);
        }
    }

Ya no podemos usar la siguiente acción POST en presencia de System.Text.Json:

[HttpPost]
public async Task<IActionResult> SubmitAsync(dynamic model)

En su lugar, tuvimos que usar el siguiente método, pero no era sencillo usar 'modelo' en el código de backend descendente:

[HttpPost]
public async Task<IActionResult> SubmitAsync(JsonElement model)

'modelo' es un objeto complejo y puede contener una colección de objetos y / u otros objetos complejos anidados. Para poder concluir un objeto dynamic de JsonElement , tuvimos que llamar a model.GetRawText () y hacer que Newtonsoft lo decodificara en un objeto dinámico. Esta no es la forma deseada porque el objetivo principal de este ejercicio era retirar Newtonsoft.json del proyecto.

¿Puedo asumir que abordar este ticket / problema implica una solución para nuestro problema que hemos estado experimentando? Parece ser un problema un poco urgente de abordar, así que, ¿se puede abordar más temprano que tarde?

/ cc @ahsonkhan @terrajobst

.NET Core 3.0 JsonSerializer.Deserialize a objeto dinámico

Soporte de JsonSerializer para ExpandoObject (medidas provisionales)
Soy novato, muchos lugares no son perfectos, invitamos a todos a modificar
.net Core3 sin soporte

Agregar el convertidor Json

agregar usando:

  • usando System.Text.Json;
  • usando System.Text.Json.Serialization;

`` C #
///


/// Convertidor dinámico de temperatura
/// por: [email protected]
///

clase pública DynamicJsonConverter: JsonConverter
{
lectura dinámica de anulación pública (ref Utf8JsonReader reader,
Escriba typeToConvert,
Opciones de JsonSerializerOptions)
{

        if (reader.TokenType == JsonTokenType.True)
        {
            return true;
        }

        if (reader.TokenType == JsonTokenType.False)
        {
            return false;
        }

        if (reader.TokenType == JsonTokenType.Number)
        {
            if (reader.TryGetInt64(out long l))
            {
                return l;
            }

            return reader.GetDouble();
        }

        if (reader.TokenType == JsonTokenType.String)
        {
            if (reader.TryGetDateTime(out DateTime datetime))
            {
                return datetime;
            }

            return reader.GetString();
        }

        if (reader.TokenType == JsonTokenType.StartObject)
        {
            using JsonDocument documentV = JsonDocument.ParseValue(ref reader);
            return ReadObject(documentV.RootElement);
        }
        // Use JsonElement as fallback.
        // Newtonsoft uses JArray or JObject.
        JsonDocument document = JsonDocument.ParseValue(ref reader);
        return document.RootElement.Clone();
    }

    private object ReadObject(JsonElement jsonElement)
    {
        IDictionary<string, object> expandoObject = new ExpandoObject();
        foreach (var obj in jsonElement.EnumerateObject())
        {
            var k = obj.Name;
            var value = ReadValue(obj.Value);
            expandoObject[k] = value;
        }
        return expandoObject;
    }
    private object? ReadValue(JsonElement jsonElement)
    {
        object? result = null;
        switch (jsonElement.ValueKind)
        {
            case JsonValueKind.Object:
                result = ReadObject(jsonElement);
                break;
            case JsonValueKind.Array:
                result = ReadList(jsonElement);
                break;
            case JsonValueKind.String:
                //TODO: Missing Datetime&Bytes Convert
                result = jsonElement.GetString();
                break;
            case JsonValueKind.Number:
                //TODO: more num type
                result = 0;
                if (jsonElement.TryGetInt64(out long l))
                {
                    result = l;
                }
                break;
            case JsonValueKind.True:
                result = true;
                break;
            case JsonValueKind.False:
                result = false;
                break;
            case JsonValueKind.Undefined:
            case JsonValueKind.Null:
                result = null;
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }
        return result;
    }

    private object? ReadList(JsonElement jsonElement)
    {
        IList<object?> list = new List<object?>();
        foreach (var item in jsonElement.EnumerateArray())
        {
            list.Add(ReadValue(item));
        }
        return list.Count == 0 ? null : list;
    }
    public override void Write(Utf8JsonWriter writer,
        object value,
        JsonSerializerOptions options)
    {
       // writer.WriteStringValue(value.ToString());
    }
}
## How to Use?

```C#
var serializerOptions = new JsonSerializerOptions
{
    Converters = { new DynamicJsonConverter() }
};
return JsonSerializer.Deserialize<dynamic>("{OK:"200"}", serializerOptions);

@tchivs , su solución funcionó para mí; pero como la propiedad Converters es de solo lectura, tuve que hacer algo como esto:
c# var serializerOptions = new JsonSerializerOptions(); serializerOptions.Converters.Add(new DynamicJsonConverter()); return JsonSerializer.Deserialize<dynamic>("{OK:"200"}", serializerOptions);

Intente usar el tipo JsonElement:

public JsonElement MyProperty {get; set;}

@tchivs , hice algunas modificaciones a su código, modificándolo para que use tipos de "proyección" generados dinámicamente (que tienen un subconjunto de propiedades de un tipo base), en lugar de ExpandoObject. Publiqué el código (en un proyecto de consola de muestra) aquí: EDennis.DynamicDeserialization .

Probaré este enfoque con objetos más complicados y en varios escenarios (por ejemplo, deserialización de objetos dinámicos utilizados para parchear entidades EF Core existentes y completamente tipadas; deserialización de objetos json y matrices para casos de prueba). Avíseme si lo encuentra útil o si ve algo problemático.

Gracias por la solución de la comunidad. Es sorprendente que Microsoft no pueda implementar esta función en un plazo razonable. Trabajar con respuestas GraphQL sin un objeto dinámico de algún tipo conduce a una gran cantidad de código detallado y feo. O incluso simplemente comprobar la existencia de propiedades profundamente anidadas.

Leí el hilo de comentarios y la mayoría se centran en la deserialización, me enfrento a un problema en el que también la serialización de objetos dinámicos aparentemente "falla" silenciosamente. En un intento de reproducir mi escenario, se me ocurrió la siguiente reproducción mínima:

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Text.Json;

namespace SampleConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            dynamic d = new CustomDynamicObject();
            d.AProperty = 10;
            var s = JsonSerializer.Serialize(d);

            Console.WriteLine(s);
            Console.Read();
        }
    }

    class CustomDynamicObject : DynamicObject 
    {
        private readonly IDictionary<string, object> properties = new Dictionary<string, object>();

        public override bool TryGetMember(GetMemberBinder binder, out object result) => properties.TryGetValue(binder.Name, out result);

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            properties[binder.Name] = value;
            return true;
        }

        public override IEnumerable<string> GetDynamicMemberNames()
        {
            foreach (var item in properties.Keys)
            {
                yield return item;
            }
        }
    }
}

Cuando se ejecuta s es {} , entonces la serialización no falla pero produce un objeto json vacío.

¿Es este el problema correcto? ¿O debería subir / seguir a uno diferente?

¿Es este el problema correcto? ¿O debería subir / seguir a uno diferente?

Este es el tema correcto. No es necesario abrir uno para las dos mitades de la misma función. Este problema se trata de agregar soporte para expandir objetos (lo que significa para ambos lados, serializar y deserializar).

Me encontré con este problema hoy: quería decir que JsonElement funcionaría bien si no dependiera de los JsonDocument desechados. Una forma en la que puedo pensar en solucionar este problema es implementar un destructor para JsonDocument para que su eliminación pueda posponerse para más adelante, una vez que se recopilen todos los objetos JsonElement .

También necesitaba un objeto dinámico. Sin embargo, aún no está implementado. Mi solución fue usar un diccionario.
JsonSerializer.Deserialize<Dictionary<string, string>>(response)
Y luego busque la clave que necesito :)

Solo por mi propia cordura: ¿el problema está dentro del alcance de 5.0 específicamente para compatibilidad con ExpandoObject o la capacidad de deserialización en un objeto dinámico? La conversación aquí no parece coincidir con el título del problema, y ​​definitivamente necesito este último. En mi caso, estoy deserializando una dinámica anidada, específicamente List<Dictionary<string, dynamic>> . Cambiado de nuevo a Newtonsoft por ahora :(

solo quería compartir algunos mods de azúcar de sintaxis c # 8 del código útil de @tchivs :)

/// <summary>
/// Temp Dynamic Converter with c# 8
/// by:[email protected]
/// </summary>
public class DynamicJsonConverter : JsonConverter<dynamic>
{
    public override dynamic Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        => reader.TokenType switch
        {
            JsonTokenType.True => true,
            JsonTokenType.False => false,
            JsonTokenType.Number => reader.TryGetInt64(out long l) ? 1 : reader.GetDouble(),
            JsonTokenType.String => reader.TryGetDateTime(out DateTime datetime) ? datetime.ToString() : reader.GetString(),
            JsonTokenType.StartObject =>  ReadObject(JsonDocument.ParseValue(ref reader).RootElement),
            // Use JsonElement as fallback.
                _ =>JsonDocument.ParseValue(ref reader).RootElement.Clone()
        };

    private object ReadObject(JsonElement jsonElement)
    {
        IDictionary<string, object> expandoObject = new ExpandoObject();
        foreach (var obj in jsonElement.EnumerateObject())
        {
            var k = obj.Name;
            var value = ReadValue(obj.Value);
            expandoObject[k] = value;
        }
        return expandoObject;
    }
    private object? ReadValue(JsonElement jsonElement)
        =>
         jsonElement.ValueKind switch
        {
            JsonValueKind.Object => ReadObject(jsonElement),
            JsonValueKind.Array => ReadList(jsonElement),
            JsonValueKind.String => jsonElement.GetString(),
            JsonValueKind.Number =>  jsonElement.TryGetInt64(out long l) ? 1 :0,
            JsonValueKind.True => true,
            JsonValueKind.False =>false,
            JsonValueKind.Undefined => null,
            JsonValueKind.Null => null,
                _ => throw new ArgumentOutOfRangeException()
        };

    private object? ReadList(JsonElement jsonElement)
    {
        var list = new List<object?>();
        jsonElement.EnumerateArray().ToList().ForEach(j => list.Add(ReadValue(j)));
        return list.Count == 0 ? null : list;
    }

    public override void Write(Utf8JsonWriter writer,
        object value,
        JsonSerializerOptions options)
        {
        // writer.WriteStringValue(value.ToString());
        }
}

@ rs38 gracias por el código aquí, era exactamente lo que necesitaba. Quería señalar un cambio muy sutil pero importante que se necesitaba. Las dos líneas que analizan el tipo "Número" son incorrectas en su versión comprimida:

JsonTokenType.Number => reader.TryGetInt64(out long l) ? 1 : reader.GetDouble(),

debiera ser

JsonTokenType.Number => reader.TryGetInt64(out long l) ? l : reader.GetDouble(),

JsonValueKind.Number => jsonElement.TryGetInt64(out long l) ? 1 :0,

debiera ser

JsonValueKind.Number => jsonElement.TryGetInt64(out long l) ? l :0,

@layomia Esto no es grave. El soporte debería haberse realizado hace mucho tiempo (de hecho, System.Text.Json no debería haberse lanzado sin él, en mi opinión). ¡Y ni siquiera tenemos una fecha límite!

Mi escenario es sobre CosmosDB. Estoy consultando usando el nuevo .NET SDK que usa este JsonSerializer y dado que es una base de datos sin esquema, no quiero crear una clase para cada proyección de los datos de la base de datos que necesito hacer (hay un montón de consultas diferentes) .
Necesito los resultados de las consultas como listas de objetos dinámicos.

@ SocVi100 No pongas tus esperanzas en Microsoft para este. Mejor quédate con Json.net de Newtonsoft. @layomia ha

¿Por qué no contribuyes? implementarlo y poner un PR!

https://github.com/dotnet/runtime/blob/master/docs/coding-guidelines/adding-api-guidelines.md
https://github.com/dotnet/runtime/blob/master/docs/area-owners.md (
@ericstj |, propietarios: @layomia @steveharter @jozkee)

¿Por qué no contribuyes? implementarlo y poner un PR!

Ojalá pudiera.

Mi escenario es sobre CosmosDB. Estoy consultando usando el nuevo .NET SDK que usa este JsonSerializer y dado que es una base de datos sin esquema, no quiero crear una clase para cada proyección de los datos de la base de datos que necesito hacer (hay un montón de consultas diferentes) .
Necesito los resultados de las consultas como listas de objetos dinámicos.

Es posible configurar un serializador diferente, usando CosmosClientBuilder.WithCustomSerializer .

Aquí hay un ejemplo: CosmosJsonNetSerializer

No pudimos encajar esta función en 5.0. Tuvimos que priorizar junto con el resto del trabajo de la función 5.0, y este no encajaba. FWIW esto estaba muy cerca de la línea de corte, por lo tanto, el movimiento tardío.

@layomia ha

@RobbyDeLaet, ¿podemos intentar que esta discusión sea constructiva? Estamos tratando de hacer todo lo posible para responder a las principales características solicitadas por los clientes, manteniendo los principios de diseño. Con respecto a la paridad de características con Newtonsoft.Json, esto es lo que tenemos que decir .

System.Text.Json se centra principalmente en el rendimiento, la seguridad y el cumplimiento de los estándares. Para algunos escenarios, System.Text.Json no tiene una funcionalidad incorporada, pero existen soluciones alternativas recomendadas. Si su aplicación depende de una característica que falta, considere presentar un problema para averiguar si se puede agregar soporte para su escenario.

No pretendemos reemplazar Newtonsoft.JSON. Si funciona para usted, continúe usándolo. Haremos todo lo posible para que System.Text.Json sea lo más útil posible mientras mantenemos las ganancias que hemos logrado en rendimiento, seguridad, cumplimiento de estándares y estratificación. Con el tiempo, esperamos que funcione para la mayor cantidad de personas posible. Escucho el interés aquí y me aseguraré de que nos concentremos en este avance.

Gracias por tus sugerencias. Finalmente me fui por el medio, usando el serializador de Newtonsoft solo para la deserialización, pero todavía tengo que probar. Espero que no dure demasiado para obtener compatibilidad con ExpandoObject en el serializador predeterminado de CosmosDB para poder deshacerme del anterior.
@RobbyDeLaet , ¡gracias por tu comentario! No tengo muchas expectativas acerca de que Microsoft implemente nada, me he sentido frustrado muchas veces por eso. Como desarrollador independiente, he esperado durante años, literalmente, que se implementaran características esenciales en la infraestructura de CosmosDB, EntityFramework e Identity, y ellos se abren camino ignorándonos. No importa cuántos votos tengan en UserVoice o lo que sea. Supongo que tales características no son necesarias para sus propios desarrollos, por lo que están fuera de alcance ...
El problema es siempre el mismo: demasiado marketing = demasiadas expectativas

Para ayudar a aquellos que necesitan esta funcionalidad, consulte los ejemplos de código anteriores para ayudar a desbloquear:

  • De @tchivs (que usa ExpandoObject para objetos y List<object> para colecciones)
  • De @denmitchell (que usa IL Emit para objetos en lugar de ExpandoObject )

Para ayudar con los requisitos de esta función, proporcionaré una nueva muestra de convertidor personalizado y la vincularé aquí cuando esté lista. Después de eso, agregaré esa muestra a la @tdykstra

Un detalle aún no discutido es que el soporte de dynamic requiere una referencia al ensamblaje System.Linq.Expressions.dll muy grande; esta complicación puede significar que en el futuro agreguemos el convertidor dinámico en un nuevo ensamblaje (por ejemplo, System.Text.Json.Converters.dll ) para obtener pago por juego para aquellos que se preocupan por el tamaño de la implementación (como una aplicación autónoma o un Blazor aplicación cliente).

Solo por mi propia cordura: ¿el problema está dentro del alcance de 5.0 específicamente para compatibilidad con ExpandoObject o la capacidad de deserialización en un objeto dinámico? La conversación aquí no parece coincidir con el título del problema, y ​​definitivamente necesito este último. En mi caso, estoy deserializando una dinámica anidada, específicamente List<Dictionary<string, dynamic>> . Cambiado de nuevo a Newtonsoft por ahora :(

Ahora estoy probando una función antigua que implementé para deserializar objetos anidados y veo el mismo problema observado en este tema con "valueKind".

¿Había alguna forma de solucionar este problema utilizando los nuevos paquetes neetonsoft.json? Porque el código anterior funcionó para el objeto de nivel raíz pero no para los objetos anidados.

@ericstj Disculpas, mi reacción fue quizás demasiado negativa y dura, pero al igual que @ SocVi100 , soy un desarrollador independiente y, a veces, las frustraciones se

Solo por mi propia cordura: ¿el problema está dentro del alcance de 5.0 específicamente para el soporte de ExpandoObject o la capacidad de deserializar en un objeto dinámico?

Supongo que la semántica deseada debe tener un tipo dinámico, por lo que después de deserializar puede acceder a todas las propiedades (incluidas las anidadas) de manera tardía:

dynamic obj = JsonSerializer.Deserialize<dynamic>(json);
string s = obj.MyChildProperty.MyStringList[2];

y supongo que la semántica deseada también admite ExpandoObject explícitamente:

ExpandoObject expando = JsonSerializer.Deserialize<ExpandoObject>(json);
dynamic obj = expando;
string s = obj.MyChildProperty.MyStringList[2];

Entonces, mi comprensión del alcance:

  • Deserialize<dynamic>(json) devuelve una primitiva, una colección o un objeto. Un objeto aquí probablemente será ExpandoObject pero podría ser IDynamicMetaObjectProvider o un tipo JITed dependiendo de la implementación. Esto es diferente a lo que ocurre hoy, que siempre devuelve JsonElement .
  • Deserialize<ExpandoObject>(json) (que implementa IDynamicMetaObjectProvider ) debe crear un ExpandoObject adecuado que a su vez se puede usar con dynamic . Esto es diferente a lo que ocurre hoy en día, que solo crea propiedades de expansión para las propiedades raíz e instancias JsonElement para todas las propiedades anidadas.
  • Serialize<ExpandoObect>() funciona como se esperaba
  • Serialize<IDynamicMetaObjectProvider ()> puede implementarse o no dependiendo de si hay escenarios en los que no se usa ExpandoObject .

Semántica en 3.0 - 5.0:

  • Serializar un ExpandoObject funciona ya que ExpandoObject implementa IDictionary<string, object> y STJ serializará correctamente cada object ya sea un primitivo, una colección o un objeto.
  • Deserializar en una especie de ExpandoObject funciona, pero sólo las propiedades raíz son propiedades expando-propiedades "adecuadas"; cualquier propiedad anidada será JsonElement por lo que hay inconsistencia en el modelo de programación, por lo que debe evitarse la deserialización de ExpandoObject menos que se utilice un convertidor personalizado para eso.

Opciones para 3.0-5.0:

  • Escriba un convertidor personalizado para ExpandoObject y \ o object como se menciona en las publicaciones anteriores; Pronto proporcionaré un enlace a una nueva muestra que idealmente se asigna a una función 6.0.
  • Si solo serializa, no deserializa, puede usar ExpandoObject (o dynamic si el tipo se basa en ExpandoObject ). Dado que deserializar ExpandoObject tiene un problema de inconsistencia hoy en STJ, ExpandoObject no se recomienda si se deserializa.
  • Utilice JsonElement lugar de objetos dinámicos. JsonElement no intenta "adivinar" a qué tipo se asigna JSON, por lo que debe ser explícito llamando a GetString (), GetInt32 (), etc.

Además, según la implementación, la implementación de un convertidor personalizado probablemente signifique que hay algunas "conjeturas" sobre los tipos de CLR a los que se asigna JSON durante la deserialización. Por ejemplo, una cadena JSON puede correlacionarse con un DateTime o un string , un número JSON puede correlacionarse con un double o un long , y el Es necesario determinar el tipo de matriz JSON. Esto debe ser examinado y comparado con la semántica de Newtonsoft. Esto también debe alinearse con el tipo que se devuelve cuando una propiedad es de Tipo object (hoy es JsonElement ).

Aquí hay algunos ejemplos de semántica de STJ 3.0 - 5.0:

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Diagnostics;
using System.Linq;
using System.Text.Json;

namespace ConsoleApp
{
    class Program
    {
        const string ExpectedJson = "{\"A\":\"A\",\"B\":[1,2],\"C\":42,\"D\":\"2020-01-01T00:00:00\",\"E\":{\"A_Child\":\"A_Child\"}}";

        static void Main(string[] args)
        {
            DateTime dateTime = new DateTime(2020, 1, 1);

            dynamic myDynamicChild = new ExpandoObject();
            myDynamicChild.A_Child = "A_Child";

            dynamic myDynamic = new ExpandoObject();
            myDynamic.A = "A";
            myDynamic.B = new List<int>() { 1, 2 };
            myDynamic.C = 42;
            myDynamic.D = dateTime;
            myDynamic.E = myDynamicChild;

            // Verify we can call late-bound property.
            int c = myDynamic.C;
            Debug.Assert(c == 42);

            // STJ can serialize with ExpandoObject since it implements IDictionary<string, object>.
            string json = JsonSerializer.Serialize<ExpandoObject>(myDynamic);
            Debug.Assert(json == ExpectedJson);

            // Using 'dynamic' against backing ExpandoObject works.
            json = JsonSerializer.Serialize<dynamic>(myDynamic);
            Debug.Assert(json == ExpectedJson);

            // Deserialize with <dynamic>, <object> and <JsonElement>.
            // For 5.0, using one of these is recommended over ExpandoObject because the programming model will be
            // consistent for the root type and all nested types.
            // Using <JsonElement> makes it clear and non-abiguous.
            // Using <object> by default uses 'JsonElement', but can be overridden by a custom converter.
            // Using <dynamic> uses 'object' which uses 'JsonElement'.
            {
                dynamic d = JsonSerializer.Deserialize<dynamic>(json);
                VerifyJsonElement(d);

                try
                {
                    // We will get an exception here if we try to access a dynamic property since 'object' is deserialized
                    // as a JsonElement and not an ExpandoObject.
                    c = d.C;
                    Debug.Fail("Should have thrown Exception!");
                }
                catch (Exception ex)
                {
                    Debug.Assert(ex.Message == "'System.Text.Json.JsonElement' does not contain a definition for 'C'");
                }

                // Serializing with <object> creates a JsonElement by default (can be changed by a custom converter).
                object o = JsonSerializer.Deserialize<object>(json);
                VerifyJsonElement((JsonElement)o);

                // Serialize with explicit <JsonElement>.
                JsonElement e = JsonSerializer.Deserialize<JsonElement>(json);
                VerifyJsonElement(e);
            }

            // Deserialize with ExpandoObject. This creates an ExpandoObject with the root Type having Expando-properties
            // but the value of those properties will be JsonElement. All other nested properties\objects\collections will
            // also be JsonElement. Due to the inconsistency of having only root-level Expando-properties (such as A_Child),
            // deserializing as ExpandoObject is not recommended (unless a ExpandoObject custom converter is used).
            {
                // When STJ deserializes, it creates ExpandoObjects via IDictionary<string, object> where 'object' is JsonElement.
                ExpandoObject expando = JsonSerializer.Deserialize<ExpandoObject>(json);
                Debug.Assert(((IDictionary<string, object>)expando).Keys.Count == 5);
                myDynamic = expando;

                JsonElement jsonElement = myDynamic.A;
                Debug.Assert(jsonElement.GetString() == "A");

                jsonElement = myDynamic.B;
                Debug.Assert(jsonElement.EnumerateArray().Count() == 2);

                jsonElement = myDynamic.C;
                Debug.Assert(jsonElement.GetInt32() == 42);

                jsonElement = myDynamic.D;
                Debug.Assert(jsonElement.GetDateTime() == dateTime);

                jsonElement = myDynamic.E;
                // Here we have an inconsistency. Nested object property must use JsonElement (not a dynamic property).
                Debug.Assert(jsonElement.GetProperty("A_Child").GetString() == "A_Child");

                // Re-serialize works as expected.
                json = JsonSerializer.Serialize<ExpandoObject>(myDynamic);
                Debug.Assert(json == ExpectedJson);

                // Re-serialize works as expected; dynamic works here since backed by ExpandoObject in this example.
                json = JsonSerializer.Serialize<dynamic>(myDynamic);
                Debug.Assert(json == ExpectedJson);
            }

            void VerifyJsonElement(JsonElement elem)
            {
                // Verify JsonElement
                Debug.Assert(elem.GetProperty("A").GetString() == "A");
                Debug.Assert(elem.GetProperty("B").EnumerateArray().Count() == 2);
                Debug.Assert(elem.GetProperty("C").GetInt32() == 42);
                Debug.Assert(elem.GetProperty("D").GetDateTime() == dateTime);
                Debug.Assert(elem.GetProperty("E").GetProperty("A_Child").GetString() == "A_Child");

                // Re-serialize
                json = JsonSerializer.Serialize<dynamic>(elem);
                Debug.Assert(json == ExpectedJson);

                json = JsonSerializer.Serialize<JsonElement>(elem);
                Debug.Assert(json == ExpectedJson);
            }
        }
    }
}

@ rs38 ¿Puede corregir su fragmento de código basándose en lo que señaló @ ryan-hollister-q2?

Según lo prometido, el RP que proporciona una muestra de implementación dinámica se encuentra en https://github.com/dotnet/runtime/pull/42097.

@ rs38 ¿Puede corregir su fragmento de código basándose en lo que señaló @ ryan-hollister-q2?

ese no era mi código, solo compartí algunos fragmentos de @tchivs anteriormente en este hilo.

¿Fue útil esta página
0 / 5 - 0 calificaciones