Aws-lambda-dotnet: Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer usa una propiedad diferente a la de Amazon.Lambda.Serialization.Json.JsonSerializer

Creado en 15 abr. 2020  ·  43Comentarios  ·  Fuente: aws/aws-lambda-dotnet

Parece que el comportamiento predeterminado de la carcasa ha cambiado entre Amazon.Lambda.Serialization.Json.JsonSerializer y Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer .

Aquí hay un código de muestra para probar la diferencia:

// create an instance to serialize
var record = new Record {
    Foo = "Hello world!"
};

// show serialization with original Lambda serializer based on Newtonsoft.Json
var oldSerializer = SerializeWith(record, new Amazon.Lambda.Serialization.Json.JsonSerializer());
Console.WriteLine($"Amazon.Lambda.Serialization.Json.JsonSerializer: {oldSerializer}");

// show serialization with new Lambda serializer based on System.Text.Json
var newSerializer = SerializeWith(record, new Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer());
Console.WriteLine($"Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer: {newSerializer}");

// show serialization with System.Json.Text
var jsonTextSerializer = System.Text.Json.JsonSerializer.Serialize<Record>(record);
Console.WriteLine($"System.Text.Json.JsonSerializer: {jsonTextSerializer}");

// local functions
string SerializeWith<T>(T value, Amazon.Lambda.Core.ILambdaSerializer serializer) {
    using var buffer = new MemoryStream();
    serializer.Serialize<T>(value, buffer);;
    return System.Text.Encoding.UTF8.GetString(buffer.ToArray());
}

El código anterior produce el siguiente resultado:

Amazon.Lambda.Serialization.Json.JsonSerializer: {"Foo":"Hello world!"}
Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer: {"foo":"Hello world!"}
System.Text.Json.JsonSerializer: {"Foo":"Hello world!"}
bug

Comentario más útil

¿Es posible mantener este tema en el tema?

Todos 43 comentarios

De acuerdo, no debería haber cambiado la carcasa entre las 2 bibliotecas. Creo que nos faltan pruebas para solicitudes y respuestas personalizadas, ya que las pruebas ahora se centran principalmente en eventos de AWS.

Ahora que esto se ha enviado, cambiar el comportamiento predeterminado es realmente inviable. Mi sugerencia es agregar un nuevo constructor que incluya una enumeración para el estilo de la carcasa para que pueda declarar la carcasa que desea usar. Luego podría actualizar las plantillas para usar el nuevo constructor. ¿Cómo te sientes con ese trabajo?

No sé cuál fue la idea aquí. Entiendo que no quiere quebrar a las personas que pueden haber tomado una dependencia. Parece que el error fue creer que AWS tiene coherencia en la asignación de nombres a los campos JSON (es decir, AWSNamingPolicy ). No es asi. Algunos servicios utilizan Pascal-casing, como CloudFormation:
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref-responses.html

Cambiar la carcasa automáticamente y no respetar el comportamiento predeterminado System.Text.Json es un defecto fatal, en mi humilde opinión. Tal vez considere lanzar un Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializerV2 y poner el viejo en hielo.

Para aclarar, como no estoy familiarizado con cómo funciona esto, la declaración de atributos de ensamblaje solo se usa para la deserialización, ¿correcto?

[assembly: LambdaSerializer(typeof(Amazon.Lambda.SystemTextJson.LambdaJsonSerializer))]

Sin embargo, ¿es necesario si utilizo esta firma de controlador?

Task<Stream> FunctionHandlerAsync(Stream stream, ILambdaContext context)

¿Qué sucede si no hay una declaración LambdaSerializerAttribute para el ensamblaje o el método de punto de entrada?

@normj, una opción puede ser agregar atributos para nombrar explícitamente las propiedades json y, por lo tanto, respetar el nombre independientemente de la mayúscula

Estoy de acuerdo con esa sugerencia: agregarlos es un trabajo único y tedioso, pero siempre son correctos.

@normj En mi

@ 3GDXC Estoy de acuerdo en que es un error de implementación. Solo estoy pensando en la solución. Actualmente en el paquete tenemos un serializador llamado LambdaJsonSerializer . ¿Qué pasa si agregamos PascalCaseLambdaJsonSerializer y CamelCaseLambdaJsonSerializer serializador que se extienden desde LambdaJsonSerializer ? Podría cambiar las plantillas para usar PascalCaseLambdaJsonSerializer para mantener el comportamiento existente. Es una especie de versión más explícita de la sugerencia de @bjorg de tener un LambdaJsonSerializerV2.

@normj En mi

Entiendo perfectamente por qué es reacio a introducir un cambio más importante (con la corrección) en cuestión de días desde el lanzamiento de la compatibilidad con .NET 3.1; y si nosotros (royal we) tuviéramos una serie de problemas UP-FOR-GRABS sin pruebas de unidad / regresión, la comunidad podría ayudar con estos para hacer que la implementación evolucione y se pruebe antes del lanzamiento.

Feliz de ayudar si es necesario, solo diga la palabra.

un gran trabajo hasta ahora, me encanta ver crecer el soporte básico de aws lamba y .net

@ 3GDXC Tenga en cuenta que el uso de https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.APIGatewayEvents/APIGatewayProxyResponse. cs # L18

El problema es para los objetos de respuesta que crean otras personas donde no controlo si usan el atributo JsonPropertyName o no.

@normj buen punto; Puede ser que el aviso también debe incluir SIEMPRE use los atributos de JsonPropertyName para hacer cumplir el nombre explícito de sus propiedades y / o contratos de datos (mejores prácticas)

@normj, una alternativa puede ser abstraer las JsonSerializerOptions en una clase LambdaSerializerOptions y agregar las opciones como un parámetro de constructor en el atributo para que el serializador pueda tener opciones personalizadas que el desarrollador puede anular a nivel de ensamblaje / método

¿Qué hay de marcarlo como una regresión y arreglarlo como un cambio radical ahora en lugar de propagar más daño? Lo llamo _harm_ porque LambdaJsonSerializer , que se basa en System.Text.Json , no respeta el comportamiento predeterminado de cómo serializar propiedades. Por supuesto, usar [JsonPropertyName] soluciona, pero requerir que todos hagan algo para contrarrestar un comportamiento no deseado parece una mano dura.

¿Cuántas personas seguirán encontrándose con esto como _?!?!? _ Momento en que adopten LambdaJsonSerializer como su serializador estándar?

Hola, me encuentro con este problema en Step Functions después de cambiar las tareas lambda a .Net 3.1 + el nuevo serializador. Está causando estragos porque la salida ahora está en camelcase, por lo que la siguiente forma de máquina de estado intenta evaluar el nuevo JSON utilizando el lenguaje de estados de Amazon y lanza una excepción de función de paso.

Hay una solución kludgy por el momento. Al configurar LAMBDA_NET_SERIALIZER_DEBUG=true en la variable de entorno, _.options nunca se establece en el serializador, lo que hace que el caso se devuelva intacto. No estoy seguro de si eso dará como resultado otras repercusiones además de la emisión de JSON adicional en los registros de Cloudwatch.
https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/LambdaJsonSerializer.cs#L69 -L90

En mi opinión, sería una molestia decorar todos nuestros modelos con la decoración [JsonPropertyName] ya que nuestros modelos están enterrados en varias bibliotecas nuget. Idealmente, me gustaría que el comportamiento predeterminado volviera al PascalCasing original como antes, pero estoy de acuerdo con el uso de un PascalCaseLambdaJsonSerializer explícito con las lambdas cuando se llama en nuestro proyecto Step Function.

¡Gracias!

Estoy bastante seguro de que la solución kludgy es un error.

Buen punto sobre estructuras de datos definidas por ensamblajes ascendentes.

No sé qué efecto secundario tendría para otras cosas sin [JsonPropertyName] atributos, pero el uso del constructor LambdaJsonSerializer que le permite personalizar el serializador JSON se puede revertir al comportamiento predeterminado de PascalCase por estableciendo JsonSerializerOptions.PropertyNamingPolicy en null .

Vinculado al número 628 ya que está relacionado con esta discusión.

Creo que esto está relacionado.

Mirando APIGatewayProxyRequest.cs , noté que no hay anotaciones [JsonPropertyName] . La única razón por la que esto funciona es porque a) LambdaJsonSerializer defecto es la deserialización que no distingue entre mayúsculas y minúsculas yb) LambdaJsonSerializer usa camel-casing en la serialización.

Puedo ver cómo esto ahorró muchas horas al anotar todas las clases de solicitud / respuesta en los diversos ensamblados auxiliares, pero significa que cuando usamos los ensamblados auxiliares, tenemos que usar LambdaJsonSerializer en nuestras funciones.

En retrospectiva, ¿no tendría más sentido colocar la anotación [LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))] en la clase POCO utilizada por el controlador de funciones en lugar de la clase de función en sí? Parece que, en última instancia, la función debe usar el serializador que coincida con las clases de solicitud / respuesta.

@bjorg En mi opinión, POCO es el lugar incorrecto para tener metadatos de atributos sobre la serialización que se va a utilizar; el POCO solo debe preocuparse por su dominio, es decir, los atributos de validación del modelo y el tipo / nombre de propiedad; La serialización debe respetar / utilizar estas anotaciones de modelo.

En mi humilde opinión, el atributo LambdaSerializer debe cambiarse para aceptar el tipo de serialización, por ejemplo. Amazon.Lambda.Serialization.Json.JsonSerializer con opciones de serialización opcionales; si no se proporciona la opción, se utilizarán los ajustes predeterminados y compatibles.

Pero el POCO debe estar anotado con los atributos de serializador correctos: [JsonProperty] para Newtonsoft y [JsonPropertyName] para System.Text.Json. En consecuencia, el POCO está vinculado al serializador.

Parece que [DataMember] será compatible con Newtonsoft.Json y System.Text.Json . Sin embargo, no hasta .NET 5 para este último. :(
https://github.com/dotnet/runtime/issues/29975

Mientras tanto, una solución sería anotar todos los POCO con [DataMember] y [JsonPropertyName] . Ambos atributos están definidos en .NET Core 3.1 y, por lo tanto, no requieren dependencias externas adicionales.

¿No garantizaría esto una serialización / deserialización consistente para todas las clases, al menos para los nombres de propiedad? Los convertidores deberían estar registrados por las implementaciones ILambdaSerialize .

El problema vinculado allí está cerrado. Mi comprensión de la lectura superficial de ese hilo es que no tienen la intención de apoyarlo: https://github.com/dotnet/runtime/issues/29975#issuecomment -609985802

Sí. Leí mal. Vi el enlace 5.0 y llegué a la conclusión equivocada.

Publiqué un PR https://github.com/aws/aws-lambda-dotnet/pull/636 para abordar el problema. Agradecería sus comentarios o, mejor aún, descargar la versión de vista previa desde el enlace en el PR y ayudar a verificar que el cambio funcione para usted.

Primero, gracias por abordar esto tan rápidamente. Siento haber arruinado tu sábado.

A primera vista, se ve bien. Actualmente estoy en el proceso de eliminar todas las referencias Newtonsoft.Json y, desafortunadamente, no estoy en un estado en el que pueda verificar la solución. Por ahora, simplemente copié la clase de serializador problemática y eliminé la declaración ofensiva. Con suerte, para mañana, EOD, puedo probar este cambio en mi rama de desarrollo.

Lo primero que me viene a la mente es la posibilidad de que falten anotaciones. ¿Hubo alguna estructura de datos de respuesta que no usara [DataMember] y en su lugar se basara en la carcasa de camello implícita?

@bjorg No te preocupes. Después de una semana de reuniones, redacción de documentos y ayudando a los niños con la escuela, fue muy reconfortante tener unos momentos de tranquilidad y escribir algo de programación los sábados.

Teníamos el potencial de perder la anotación [DataMember] con el serializador Newtonsoft. No estoy muy preocupado por eso porque para los tipos conocidos tenemos la prueba para ello. En este caso, a mi brecha le faltaban pruebas sobre respuestas personalizadas.

¿Sería posible lanzar un -rc1 ? La alternativa, AFAIK, es para mí piratear mis archivos _.csproj_ con las constantes de compilación adecuadas habilitadas. ¿Hay otra manera?

@bjorg En PR hay un enlace a un archivo zip que contiene paquetes NuGet prediseñados, ¿puede configurar una fuente NuGet local y poner los paquetes allí?

Hoy aprendí algo nuevo: cómo tener un feed local. Resultó ser muy fácil en .NET Core (consulte el artículo SO ).

Mi comentario más pertinente es exponer _options como una propiedad Options protegida / pública para que una clase derivada pueda usarla también.

De lo contrario, todo genial con este nuevo código de mi parte. ¡Gracias!

@normj avíseme si / cuando hay paquetes nuget actualizados. Feliz de darle otra prueba.

https://github.com/aws/aws-lambda-dotnet/issues/544#issuecomment -567780775

^ ¿Es porque no usar carcasa de camello rompe API Gateway?

Como Pascal funciona bien si la lambda está en ALB, pero no funciona en API Gateway, esta inconsistencia es confusa. ¿Cómo funcionaba esto antes de pasar a system.text?

Este es un cambio rotundo; La interfaz no es compatible. Debido a la falta de pruebas de integración y / o una revisión de la comunidad de candidatos a la versión, esta violación del principio de segregación de interfaz se ha deslizado. Cuanto más tiempo se deje sin resolver, mayor será el costo y las horas de trabajo desperdiciadas que esto ocasionará a los clientes de AWS, lo que tendrá como resultado un impacto en la reputación de AWS.

Le recomiendo que marque esta versión como rota y desaliente la migración a 3.1 para todos los desarrolladores hasta que se acuerde una solución.
Además, también recomiendo que la comunidad discuta y pruebe cualquier solución para reducir la probabilidad de agravar aún más el problema.

@lukebrowell El trabajo para el serializador se realizó en público y el PR se presentó en enero. https://github.com/aws/aws-lambda-dotnet/pull/568 El RP adjuntó un paquete prediseñado para realizar pruebas que podrían haberse realizado con tiempos de ejecución Lambda personalizados.

Estamos discutiendo la solución aquí junto con el PR y agradezco los comentarios sobre la solución propuesta https://github.com/aws/aws-lambda-dotnet/pull/636

Estoy de acuerdo en que este es un cambio importante y me decepciona que suceda, pero no estoy de acuerdo con la gravedad que sugiere. El problema solo afecta a las funciones Lambda que devuelven respuestas personalizadas, no todas las funciones Lambda y el Amazon.Lambda.Serialization.Json existente funciona igual, por lo que no creo que sea justo decir que toda la versión 3.1 Lambda está rota. Pero nuevamente entiendo la frustración y lamento que este error se haya escapado.

Espero impulsar los cambios en las relaciones públicas a principios de la semana que viene, a menos que se produzcan comentarios importantes sobre el cambio que provoquen un retraso en el lanzamiento.

@bjorg El PR se ha actualizado con un enlace a la vista previa2 de los paquetes prediseñados. https://normj-packages.s3.us-west-2.amazonaws.com/rework-serialization-preview2.zip

@normj Me doy cuenta de que la comunidad es en parte responsable aquí con respecto a omitir estos problemas, ya que (nosotros) presionamos para publicar / admitir el tiempo de ejecución .netcore 3.1 como una imagen lambda oficial y no había informado de esto ni habíamos dado comentarios. En mi humilde opinión, si bien entiendo su punto de vista con respecto a los comentarios de @lukebrowell , estaría de acuerdo en parte con @lukebrowell y sugeriría que se

Me encantaría ver una comunidad más fuerte. He estado pasando el rato en awsdevelopers.slack.com, pero el canal #dotnet es un poco silencioso. ¿Hay otro lugar donde la gente de Lambda .NET Core se reúna?

@bjorg me

@bjorg ¿es posible obtener una invitación?

Obteniendo un enlace de invitación de los moderadores. Lo publicaré aquí.

¿Es posible mantener este tema en el tema?

De acuerdo, mantengamos esto en el tema. Creé un problema comunitario n. ° 647 sobre cómo comunicarse conmigo para agregarlo al grupo de inactividad de AWS.

Sí, agradezco sugerencias sobre cómo configurar mejor la comunicación comunitaria y dónde puedo mejorar mi propia comunicación y cómo puedo involucrarme más.

_preview2_ me parece bien.

La versión 2.0.0 de Amazon.Lambda.Serialization.SystemTextJson está disponible con el cambio. Main take away actualiza la clase de serializador para que sea DefaultLambdaJsonSerializer .

También publiqué una publicación de blog que tiene una sección que describe el cambio.
https://aws.amazon.com/blogs/developer/one-month-update-to-net-core-3-1-lambda/

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