Aws-lambda-dotnet: Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer usa caixa de propriedade diferente de Amazon.Lambda.Serialization.Json.JsonSerializer

Criado em 15 abr. 2020  ·  43Comentários  ·  Fonte: aws/aws-lambda-dotnet

Parece que o comportamento padrão da caixa mudou entre Amazon.Lambda.Serialization.Json.JsonSerializer e Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer .

Aqui está um exemplo de código para testar a diferença:

// 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());
}

O código acima produz a seguinte saída:

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

Comentários muito úteis

É possível manter esse assunto em questão?

Todos 43 comentários

Concordo que não deveria ter trocado a caixa entre as 2 bibliotecas. Acho que faltam testes para solicitações e respostas personalizadas, já que os testes agora se concentram principalmente em eventos da AWS.

Agora que isso foi enviado, alterar o comportamento padrão é realmente inviável. Minha sugestão é adicionar um novo construtor que aceite um enum para o estilo de maiúsculas e minúsculas para que você possa declarar a capitalização que deseja usar. Eu poderia então atualizar os modelos para usar o novo construtor. Como você se sente sobre essa solução?

Não sei qual foi a ideia aqui. Eu entendo que você não quer quebrar pessoas que podem ter ficado dependentes. Parece que o erro foi acreditar que a AWS tem consistência na nomenclatura de campos JSON (ou seja, AWSNamingPolicy ). Isso não. Alguns serviços usam Pascal-casing, como CloudFormation:
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref-responses.html

Alterar maiúsculas e minúsculas e não respeitar o comportamento padrão de System.Text.Json é uma falha fatal, IMHO. Considere liberar um Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializerV2 e colocar o antigo no gelo.

Para esclarecer, como não estou familiarizado com como isso funciona, a declaração de atributo do assembly é usada apenas para desserialização, correto?

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

No entanto, será mesmo necessário se eu usar essa assinatura do manipulador?

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

O que acontece se não houver declaração LambdaSerializerAttribute para o método assembly ou de ponto de entrada?

@normj uma opção pode ser adicionar atributos para nomear explicitamente as propriedades json e, portanto, respeitar a nomenclatura, independentemente da caixa

Eu concordaria com essa sugestão - é um trabalho tedioso e único adicioná-los, mas eles estão sempre corretos.

@normj IMHO este é um problema de implementação que precisa / deve ser

@ 3GDXC Concordo que é um bug de implementação. Só pensando na solução. Atualmente no pacote temos um serializador chamado LambdaJsonSerializer . E se adicionarmos PascalCaseLambdaJsonSerializer e CamelCaseLambdaJsonSerializer serializador que se estende de LambdaJsonSerializer . Eu poderia mudar os modelos para usar PascalCaseLambdaJsonSerializer para manter o comportamento existente. É uma espécie de versão mais explícita da sugestão @bjorg de ter um LambdaJsonSerializerV2.

@normj IMHO, seria melhor e evitar confusão adicionar os atributos JsonPropertyName às mensagens para que, independentemente da configuração / opções do serializador, o Json resultante aderisse à nomenclatura do atributo.

Eu entendo perfeitamente por que você está relutante em introduzir mais uma alteração significativa (com a correção) em questão de dias após o lançamento do suporte .NET 3.1; e se nós (royal we) tivéssemos vários problemas UP-FOR-GRABS , nenhum teste de unidade / regressão, a comunidade poderia ajudar com eles para fazer a implementação evoluir e testar antes do lançamento.

Fico feliz em ajudar se / quando necessário, basta dizer a palavra.

ótimo trabalho até agora, adorando ver o crescimento do suporte do aws lamba e .net

@ 3GDXC Lembre-se de que a https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.APIGatewayEvents/APIGatewayProxyResponse. cs # L18

O problema é para objetos de resposta que outras pessoas criam onde eu não controlo se eles usam o atributo JsonPropertyName ou não.

@normj bom ponto; pode ser o aviso também deve incluir SEMPRE usar atributos JsonPropertyName para impor a nomenclatura explícita de suas propriedades e / ou contratos de dados (melhores práticas)

@normj uma alternativa pode ser abstrair o JsonSerializerOptions em uma classe LambdaSerializerOptions e adicionar as opções como um parâmetro de construtor no atributo para que o serializador possa ter opções personalizadas que o desenvolvedor pode substituir em um nível de assembly / método

Que tal sinalizá-lo como uma regressão e corrigi-lo como uma alteração significativa agora, em vez de espalhar mais danos? Eu chamo isso de _harm_ porque LambdaJsonSerializer , que é baseado em System.Text.Json , não respeita o comportamento padrão de como serializar propriedades. É claro que usar [JsonPropertyName] corrige isso, mas exigir que todos façam algo para conter um comportamento indesejado parece algo pesado.

Quantas pessoas continuarão a encontrar isso como _?!?!? _ Momento em que adotarem LambdaJsonSerializer como seu serializador padrão?

Olá, estou encontrando este problema nas funções de etapa após alternar as tarefas lambda para .Net 3.1 + o novo serializador. Está causando estragos porque a saída agora está em camelcase, então a próxima forma de máquina de estado tenta avaliar o novo JSON usando o Amazon States Language e lança uma exceção Step Function.

Há uma solução alternativa complicada para o momento. Definindo LAMBDA_NET_SERIALIZER_DEBUG=true na variável de ambiente, _.options nunca é definido no serializador, fazendo com que o caso seja retornado sem alterações. Não tenho certeza se isso resultará em outras repercussões além da emissão de JSON adicional nos Logs do Cloudwatch.
https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/LambdaJsonSerializer.cs#L69 -L90

IMO, seria um saco decorar todos os nossos modelos com a decoração [JsonPropertyName] pois nossos modelos estão enterrados em várias bibliotecas de nugets. Idealmente, gostaria que o comportamento padrão retornasse ao PascalCasing original como antes, mas estou bem em usar um PascalCaseLambdaJsonSerializer explícito com os lambdas quando ele é chamado em nosso projeto Step Function.

Obrigado!

Tenho certeza de que essa solução alternativa é um bug.

Bom ponto sobre as estruturas de dados definidas por assemblies upstream.

Não sei que efeito colateral teria em outras coisas sem os atributos [JsonPropertyName] , mas usar o construtor LambdaJsonSerializer que permite personalizar o serializador JSON pode ser revertido para o comportamento PascalCase padrão definindo JsonSerializerOptions.PropertyNamingPolicy para null .

Vinculando ao problema # 628, pois está relacionado a esta discussão.

Eu acho que isso está relacionado.

Olhando para APIGatewayProxyRequest.cs , percebi que não há anotações [JsonPropertyName] . A única razão pela qual isso funciona é porque a) LambdaJsonSerializer padroniza para desserialização sem distinção entre maiúsculas e minúsculas eb) LambdaJsonSerializer usa "camel-case" na serialização.

Eu posso ver como isso economizou muitas horas na anotação de todas as classes de solicitação / resposta nos vários assemblies auxiliares, mas significa que quando usamos os assemblies auxiliares, temos que usar LambdaJsonSerializer em nossas funções.

Em retrospecto, não faria mais sentido colocar a anotação [LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))] na classe POCO usada pelo manipulador de função em vez da própria classe de função? Parece que, em última análise, a função deve usar o serializador que corresponde às classes de solicitação / resposta.

@bjorg IMO o POCO é o local errado para ter metadados de atributos sobre a serialização que deve ser usada; o POCO deve se preocupar apenas com o seu domínio, ou seja, os atributos de validação do modelo e tipo / nomenclatura da propriedade; a serialização deve respeitar / usar essas anotações de modelo.

IMHO o atributo LambdaSerializer deve ser alterado para aceitar o tipo de serialização, por exemplo. Amazon.Lambda.Serialization.Json.JsonSerializer com opções de serialização opcionais; se a opção não for fornecida, as configurações padrão e compatíveis serão usadas.

Mas o POCO precisa ser anotado com os atributos corretos do serializador: [JsonProperty] para Newtonsoft e [JsonPropertyName] para System.Text.Json. Conseqüentemente, o POCO está vinculado ao serializador.

Parece que [DataMember] será suportado por Newtonsoft.Json e System.Text.Json . No entanto, não até o .NET 5 para o último. :(
https://github.com/dotnet/runtime/issues/29975

Nesse ínterim, uma solução seria anotar todos os POCOs com [DataMember] e [JsonPropertyName] . Ambos os atributos são definidos no .NET Core 3.1 e, portanto, não requerem dependências externas adicionais.

Isso não garantiria serialização / desserialização consistente para todas as classes, pelo menos para nomes de propriedades? Os conversores precisariam ser registrados pelas implementações ILambdaSerialize .

O problema vinculado aí foi encerrado. Meu entendimento da leitura superficial desse tópico é que eles não pretendem suportá-lo: https://github.com/dotnet/runtime/issues/29975#issuecomment -609985802

Sim. Eu li errado. Eu vi o link 5.0 e tirei a conclusão errada.

Publiquei um PR https://github.com/aws/aws-lambda-dotnet/pull/636 para abordar o problema. Eu gostaria de receber feedback ou, melhor ainda, baixar a versão de visualização do link no PR e ajudar a verificar se a mudança funciona para você.

Em primeiro lugar, obrigado por resolver isso tão rapidamente. Desculpe por ter arruinado seu sábado.

À primeira vista, parece bom. No momento, estou removendo todas as referências de Newtonsoft.Json e não estou em um estado em que possa verificar a correção, infelizmente. Por enquanto, simplesmente copiei a classe do serializador problemático e removi a instrução ofensiva. Espero que amanhã, EOD, eu possa testar essa alteração em meu branch de desenvolvimento.

A primeira coisa que vem à mente são anotações potencialmente ausentes. Houve alguma estrutura de dados de resposta que não usasse [DataMember] e, em vez disso, dependesse do invólucro de camelo implícito?

@bjorg Não se preocupe. Depois de uma semana de reuniões, escrevendo documentos e ajudando as crianças na escola, foi muito reconfortante ter alguns momentos de silêncio e fazer alguns códigos aos sábados.

Tínhamos o potencial de perder a anotação [DataMember] com o serializador Newtonsoft. Não estou muito preocupado com isso porque, para tipos conhecidos, temos o teste para isso. Neste caso, minha lacuna estava faltando testes em respostas personalizadas.

Seria possível liberar -rc1 ? A alternativa, AFAIK, é para mim hackear meus arquivos _.csproj_ com as constantes de compilação corretas habilitadas. Existe outra maneira?

@bjorg No PR, há um link para um arquivo zip contendo pacotes NuGet pré-construídos. Você pode configurar uma fonte NuGet local e colocar os pacotes lá?

Aprendi algo novo hoje: como ter um feed local. Acabou sendo muito fácil no .NET Core (consulte o artigo do SO ).

Meu feedback mais pertinente é expor _options como uma propriedade Options protegida / pública para que uma classe derivada também possa usá-la.

Fora isso, tudo ótimo com esse novo código da minha parte. THX!

@normj deixe-me saber se / quando houver pacotes nuget atualizados. Fico feliz em fazer outro teste.

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

^ É porque não usar a caixa do camelo quebra o API Gateway?

Cos Pascal funciona bem se lambda está em ALB, mas não funciona em API Gateway, essa inconsistência é confusa. Como isso funcionava antes da mudança para system.text?

Esta é uma mudança significativa; A interface não é compatível. Devido à falta de testes de integração e / ou revisão da comunidade de candidatos a lançamento, essa violação do princípio de segregação de interface foi perdida. Quanto mais tempo isso ficar sem solução, maior será o custo e o desperdício de horas de trabalho que isso causará aos clientes da AWS, resultando em impacto na reputação da AWS.

Recomendo que você marque esta versão como interrompida e desencoraje a migração para o 3.1 para todos os desenvolvedores até que uma correção seja acordada.
Além disso, também recomendo que qualquer correção seja discutida e totalmente testada pela comunidade para reduzir a probabilidade de agravar ainda mais o problema

@lukebrowell O trabalho para o serializador foi feito em público com o PR enviado em janeiro. https://github.com/aws/aws-lambda-dotnet/pull/568 O PR anexou um pacote pré-construído para teste que poderia ter sido feito com tempos de execução Lambda personalizados.

Estamos discutindo a correção aqui, juntamente com o PR e agradeço comentários sobre a correção proposta https://github.com/aws/aws-lambda-dotnet/pull/636

Eu concordo que essa é uma alteração significativa e estou decepcionado com isso, mas discordo sobre a gravidade que você sugere. O problema afeta apenas as funções do Lambda que retornam respostas personalizadas, nem todas as funções do Lambda e o Amazon.Lambda.Serialization.Json existente funciona da mesma forma, então não acredito que seja justo dizer que toda a versão 3.1 do Lambda está quebrada. Mas, novamente, eu entendo a frustração e sinto muito que o bug tenha escapado.

Espero promover as mudanças no PR no início da próxima semana, a menos que algum feedback significativo sobre a mudança venha a atrasar o lançamento.

@bjorg O PR foi atualizado com um link para preview2 dos pacotes pré-construídos. https://normj-packages.s3.us-west-2.amazonaws.com/rework-serialization-preview2.zip

@normj Percebo que a comunidade é parcialmente responsável aqui com relação à omissão desses problemas, pois (nós) pressionamos para liberar / dar suporte ao runtime .netcore 3.1 como uma imagem lambda oficial e não relatamos isso ou demos feedback. IMHO, embora eu entenda sua opinião com relação aos comentários de @lukebrowell e sugiro que uma unidade de trabalho seja iniciada (com envolvimento total da comunidade) para abrir a discussão sobre recursos / design da biblioteca de funções / serviços lambda aspnetcore com o objetivo de resolver quaisquer deficiências e / ou bugs que tenham sido identificados em uma mansão que ajude o desenvolvimento a avançar, já que este pacote parece um trabalho um pouco apressado TBH.

Eu adoraria ver uma comunidade mais forte. Tenho andado pelo awsdevelopers.slack.com, mas o canal #dotnet é um pouco quieto. Existe outro lugar onde o pessoal do Lambda .NET Core está se reunindo?

@bjorg vou entrar;) vejo você lá (virtualmente)

@bjorg é possível obter um convite?

Obtendo um link de convite dos moderadores. Vou postar aqui.

É possível manter esse assunto em questão?

Concordo, vamos manter isso no tópico. Eu criei um problema da comunidade # 647 sobre como entrar em contato comigo para adicioná-lo ao grupo AWS slack.

Sim, agradeço sugestões sobre como configurar melhor a comunicação da comunidade e onde posso fazer melhor sobre minha própria comunicação e como posso me envolver mais.

_preview2_ parece bom para mim.

A versão 2.0.0 de Amazon.Lambda.Serialization.SystemTextJson já saiu com a mudança. Principalmente, atualize a classe do serializador para DefaultLambdaJsonSerializer .

Também publiquei uma postagem no blog que contém uma seção que descreve a mudança.
https://aws.amazon.com/blogs/developer/one-month-update-to-net-core-3-1-lambda/

Esta página foi útil?
0 / 5 - 0 avaliações