Aws-lambda-dotnet: Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer verwendet ein anderes Eigenschaftsgehäuse als Amazon.Lambda.Serialization.Json.JsonSerializer

Erstellt am 15. Apr. 2020  ·  43Kommentare  ·  Quelle: aws/aws-lambda-dotnet

Es scheint, dass sich das Standardgehäuseverhalten zwischen Amazon.Lambda.Serialization.Json.JsonSerializer und Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializer geändert hat.

Hier ist ein Beispielcode, um den Unterschied zu testen:

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

Der obige Code erzeugt die folgende Ausgabe:

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

Hilfreichster Kommentar

Ist es möglich, dieses Problem im Thema zu halten?

Alle 43 Kommentare

Einverstanden hätte ich das Gehäuse nicht zwischen den beiden Bibliotheken wechseln sollen. Ich denke, es fehlen Tests für benutzerdefinierte Anfragen und Antworten, da sich die Tests jetzt hauptsächlich auf AWS-Ereignisse konzentrieren.

Jetzt, da dies ausgeliefert wurde, ist eine Änderung des Standardverhaltens wirklich nicht durchführbar. Mein Vorschlag ist, einen neuen Konstruktor hinzuzufügen, der eine Aufzählung für den Gehäusestil enthält, damit Sie das Gehäuse deklarieren können, das Sie verwenden möchten. Ich könnte dann die Vorlagen aktualisieren, um den neuen Konstruktor zu verwenden. Wie denkst du über diese Arbeit?

Ich weiß nicht, was die Idee hier war. Ich verstehe, dass Sie nicht Leute brechen wollen, die eine Abhängigkeit genommen haben könnten. Es scheint, dass der Fehler darin bestand zu glauben, dass AWS bei der Benennung von JSON-Feldern (dh AWSNamingPolicy ) konsistent ist. Es tut nicht. Einige Dienste verwenden Pascal-Gehäuse, z. B. CloudFormation:
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref-responses.html

Das automatische Ändern des Gehäuses und das Nichteinhalten des Standardverhaltens von System.Text.Json ist meiner Meinung nach ein schwerwiegender Fehler. Vielleicht sollten Sie ein Amazon.Lambda.Serialization.SystemTextJson.LambdaJsonSerializerV2 und das alte auf Eis legen.

Da ich nicht mit der Funktionsweise vertraut bin, wird die Assembly-Attributdeklaration nur zur Deserialisierung verwendet. Richtig?

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

Wird es jedoch überhaupt benötigt, wenn ich diese Handlersignatur verwende?

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

Was passiert, wenn für die Assembly- oder Einstiegspunktmethode keine LambdaSerializerAttribute -Deklaration vorhanden ist?

@normj Eine Option kann darin bestehen, Attribute hinzuzufügen, um die json-Eigenschaften explizit zu benennen und daher die Benennung unabhängig von der

Ich würde diesem Vorschlag zustimmen - es ist mühsam, sie einmal hinzuzufügen, aber dann sind sie immer richtig.

@normj IMHO ist dies ein Implementierungsproblem, das längerfristig als BUG

@ 3GDXC Ich stimme zu, dass es sich um einen Implementierungsfehler handelt. Ich denke nur an die Lösung. Derzeit haben wir im Paket einen Serializer namens LambdaJsonSerializer . Was ist, wenn wir PascalCaseLambdaJsonSerializer und CamelCaseLambdaJsonSerializer Serializer hinzufügen, die sich von LambdaJsonSerializer erstrecken? Ich könnte die Vorlagen so ändern, dass PascalCaseLambdaJsonSerializer , um das vorhandene Verhalten beizubehalten. Es ist eine Art explizitere Version des @ bjorg- Vorschlags, einen LambdaJsonSerializerV2 zu haben.

@normj IMHO ist es besser, Verwirrung zu vermeiden, wenn Sie die JsonPropertyName-Attribute zu den Nachrichten hinzufügen, damit der resultierende Json unabhängig von der Konfiguration / den Optionen des Serialisierers an der Attributbenennung festhält.

Ich verstehe voll und ganz, warum Sie es ablehnen, innerhalb weniger Tage nach Veröffentlichung der .NET 3.1-Unterstützung eine weitere wichtige Änderung (mit der Korrektur) einzuführen. und wenn wir (royal we) eine Reihe von UP-FOR-GRABS- Problemen hatten, konnte die Community bei diesen Tests keine Einheits- / Regressionstests durchführen, um die Implementierung weiterzuentwickeln und zu testen.

Gerne helfen wir Ihnen bei Bedarf einfach das Wort.

Bisher großartige Arbeit, ich liebe es zu sehen, wie aws lamba und .net Core Support wachsen

@ 3GDXC Beachten Sie, dass das Gehäuse nur Rückgabeobjekte betrifft, bei denen vom .NET-Objekt zur JSON-Zeichenfolge https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.APIGatewayEvents/APIGatewayProxyResponse. cs # L18

Das Problem besteht bei Antwortobjekten, die andere Personen erstellen, bei denen ich nicht steuere, ob sie das Attribut JsonPropertyName verwenden oder nicht.

@normj guter Punkt; kann sein , sollte das Beratungs auch immer JsonPropertyName verwenden gehören Attribute explizite Benennung Ihrer Eigenschaften und / oder Datenverträge (Best Practices) zu erzwingen

@normj Eine Alternative könnte darin bestehen, die JsonSerializerOptions in eine LambdaSerializerOptions-Klasse zu abstrahieren und die Optionen als Konstruktorparameter im Attribut hinzuzufügen, damit der Serializer benutzerdefinierte Optionen haben kann, die Entwickler auf Assembly- / Methodenebene überschreiben können

Wie wäre es, wenn Sie es als Regression markieren und jetzt als bahnbrechende Veränderung korrigieren, anstatt mehr Schaden zu verbreiten? Ich nenne es _harm_, weil LambdaJsonSerializer , das auf System.Text.Json basiert, das Standardverhalten beim Serialisieren von Eigenschaften nicht berücksichtigt. Natürlich behebt die Verwendung von [JsonPropertyName] Problem, aber es scheint schwerfällig zu sein, dass jeder etwas tun muss, um einem unerwünschten Verhalten entgegenzuwirken.

Wie viele Leute werden dies weiterhin als _?!?!? _ Moment erleben, wenn sie LambdaJsonSerializer als Standard-Serializer übernehmen?

Hallo, ich stoße auf dieses Problem in den Schrittfunktionen, nachdem ich die Lambda-Aufgaben auf .Net 3.1 + den neuen Serializer umgestellt habe. Es ist verheerend, weil sich die Ausgabe jetzt im Camelcase befindet, sodass die nächste Zustandsmaschinenform versucht, den neuen JSON mithilfe der Amazon States Language zu bewerten, und eine Step Function-Ausnahme auslöst.

Im Moment gibt es eine klobige Problemumgehung. Durch Festlegen von LAMBDA_NET_SERIALIZER_DEBUG=true in der Umgebungsvariablen wird _.options niemals im Serializer festgelegt, wodurch der Fall unberührt zurückgegeben wird. Ich bin nicht sicher, ob dies zu anderen Auswirkungen führen wird, als dass zusätzlicher JSON in die Cloudwatch-Protokolle ausgegeben wird.
https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.Serialization.SystemTextJson/LambdaJsonSerializer.cs#L69 -L90

IMO, es wäre mühsam, alle unsere Modelle mit der Dekoration [JsonPropertyName] dekorieren, da unsere Modelle in einer Reihe von Nuget-Bibliotheken vergraben sind. Idealerweise möchte ich, dass das Standardverhalten wie zuvor zum ursprünglichen PascalCasing zurückkehrt, aber ich kann mit den Lambdas ein explizites PascalCaseLambdaJsonSerializer wenn es in unserem Step Function-Projekt aufgerufen wird.

Vielen Dank!

Ich bin mir ziemlich sicher, dass klobige Problemumgehung ein Fehler ist.

Guter Punkt zu Datenstrukturen, die von vorgelagerten Baugruppen definiert werden.

Ich weiß nicht, welche Nebenwirkung es auf andere Dinge ohne [JsonPropertyName] -Attribute haben würde, aber die Verwendung des Konstruktors LambdaJsonSerializer , mit dem Sie den JSON-Serializer anpassen können, kann durch auf das Standardverhalten von PascalCase zurückgesetzt werden Setzen von JsonSerializerOptions.PropertyNamingPolicy auf null .

Verlinkung zu Problem Nr. 628, da es mit dieser Diskussion zusammenhängt.

Ich denke, das hängt zusammen.

Beim Betrachten von APIGatewayProxyRequest.cs habe ich festgestellt, dass keine [JsonPropertyName] -Anmerkungen vorhanden sind. Der einzige Grund, warum dies funktioniert, ist, dass a) LambdaJsonSerializer standardmäßig die Deserialisierung ohne Berücksichtigung der Groß- und Kleinschreibung verwendet und b) LambdaJsonSerializer bei der Serialisierung Kamelhüllen verwendet.

Ich kann sehen, wie dies viele Stunden beim Annotieren aller Anforderungs- / Antwortklassen in den verschiedenen Hilfsbaugruppen gespart hat, aber es bedeutet, dass wir bei Verwendung der Hilfsbaugruppen LambdaJsonSerializer in unseren Funktionen verwenden müssen.

Wäre es im Nachhinein nicht sinnvoller, die Annotation [LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))] auf die vom Funktionshandler verwendete POCO-Klasse anstatt auf die Funktionsklasse selbst zu setzen? Es scheint, dass die Funktion letztendlich den Serializer verwenden muss, der den Anforderungs- / Antwortklassen entspricht.

@bjorg IMO Der POCO ist der falsche Ort, um Attribut-Metadaten über die zu verwendende Serialisierung zu haben. Der POCO sollte sich nur mit seiner Domäne befassen, dh mit den Modellvalidierungsattributen und dem Eigenschaftstyp / der Benennung. Die Serialisierung sollte diese Modellanmerkungen berücksichtigen / verwenden.

IMHO sollte das LambdaSerializer-Attribut geändert werden, um die Art der Serialisierung zu akzeptieren, z. Amazon.Lambda.Serialization.Json.JsonSerializer mit optionalen Serialisierungsoptionen; Wenn die Option nicht angegeben ist, werden Standardeinstellungen und kompatible Einstellungen verwendet.

Der POCO muss jedoch mit den richtigen Serializer-Attributen versehen werden: [JsonProperty] für Newtonsoft und [JsonPropertyName] für System.Text.Json. Folglich ist der POCO an den Serializer gebunden.

Es sieht so aus, als würde [DataMember] sowohl von Newtonsoft.Json als auch von System.Text.Json . Allerdings erst mit .NET 5 für letzteres. :(
https://github.com/dotnet/runtime/issues/29975

In der Zwischenzeit besteht eine Lösung darin, alle POCOs mit [DataMember] und [JsonPropertyName] . Beide Attribute sind in .NET Core 3.1 definiert und erfordern daher keine zusätzlichen externen Abhängigkeiten.

Würde dies nicht eine konsistente Serialisierung / Deserialisierung für alle Klassen sicherstellen, zumindest für Eigenschaftsnamen? Konverter müssten von den Implementierungen ILambdaSerialize registriert werden.

Das dort verknüpfte Problem ist geschlossen. Nach meinem Verständnis beim Überfliegen dieses Threads beabsichtigen sie nicht, ihn zu unterstützen: https://github.com/dotnet/runtime/issues/29975#issuecomment -609985802

Ja. Ich habe falsch verstanden. Ich habe den 5.0-Link gesehen und bin zu dem falschen Schluss gekommen.

Ich habe eine PR https://github.com/aws/aws-lambda-dotnet/pull/636 veröffentlicht , um das Problem zu beheben. Ich würde mich über Feedback freuen oder besser noch über das Herunterladen des Vorschau-Builds über den Link in der PR und über die Überprüfung, ob die Änderung für Sie funktioniert.

Zunächst einmal vielen Dank, dass Sie dies so schnell angehen. Tut mir leid, dass es deinen Samstag ruiniert hat.

Auf den ersten Blick sieht es gut aus. Ich bin gerade dabei, alle Newtonsoft.Json Referenzen herauszureißen, und ich bin leider nicht in einem Zustand, in dem ich das Update überprüfen kann. Im Moment habe ich einfach die problematische Serializer-Klasse kopiert und die fehlerhafte Anweisung entfernt. Hoffentlich kann ich diese Änderung bis morgen EOD in meinem Entwicklungszweig testen.

Das erste, was mir in den Sinn kommt, sind möglicherweise fehlende Anmerkungen. Gab es Antwortdatenstrukturen, die nicht [DataMember] und sich stattdessen auf die implizite Kamelhülle stützten?

@bjorg Keine Sorge. Nach einer Woche voller Besprechungen, dem Schreiben von Dokumenten und dem Helfen von Kindern in der Schule war es sehr beruhigend, ein paar ruhige Momente zu haben und am Samstag etwas zu programmieren.

Wir hatten das Potenzial, mit dem Newtonsoft-Serializer die Anmerkung [DataMember] zu verpassen. Ich mache mir darüber keine allzu großen Sorgen, denn für bekannte Typen haben wir den Test dafür. In diesem Fall fehlten in meiner Lücke Tests zu benutzerdefinierten Antworten.

Wäre es möglich, ein -rc1 freizugeben? Die Alternative, AFAIK, ist für mich, ansonsten meine _.csproj_-Dateien mit den richtigen aktivierten Kompilierungskonstanten zu hacken. Gibt es eine andere Art und Weise?

@bjorg In der PR gibt es einen Link zu einer Zip-Datei mit vorgefertigten NuGet-Paketen. ablegen ?

Habe heute etwas Neues gelernt: wie man einen lokalen Feed hat. Es stellte sich heraus, dass es in .NET Core sehr einfach ist (siehe SO-Artikel ).

Mein relevantestes Feedback ist, _options als geschützte / öffentliche Options -Eigenschaft verfügbar zu machen, damit eine abgeleitete Klasse sie auch verwenden kann.

Ansonsten alles super mit diesem neuen Code von meiner Seite. Danke!

@normj lass mich wissen, ob / wann es aktualisierte Nuget-Pakete gibt. Gerne probiere ich es noch einmal aus.

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

^ Liegt es daran, dass die Nichtverwendung des Kamelgehäuses das API-Gateway zerstört?

Cos Pascal funktioniert einwandfrei, wenn sich das Lambda in ALB befindet, aber nicht in API Gateway. Diese Inkonsistenz ist verwirrend. Wie hat das vor dem Wechsel zu system.text funktioniert?

Dies ist eine bahnbrechende Veränderung; Die Schnittstelle ist nicht kompatibel. Aufgrund fehlender Integrationstests und / oder einer Überprüfung durch die Release-Kandidaten-Community ist dieser Verstoß gegen das Prinzip der Schnittstellentrennung durchgegangen. Je länger dies ungelöst bleibt, desto höher sind die Kosten und die verschwendeten Arbeitsstunden, die dies für AWS-Kunden verursacht, was sich auf die Reputation von AWS auswirkt.

Ich empfehle Ihnen, diese Version als fehlerhaft zu markieren und die Migration auf 3.1 für alle Entwickler zu unterbinden, bis ein Fix vereinbart ist.
Darüber hinaus empfehle ich, dass alle Korrekturen von der Community diskutiert und vollständig getestet werden, um die Wahrscheinlichkeit einer weiteren Verschärfung des Problems zu verringern

@lukebrowell Die Arbeit für den Serializer wurde in der Öffentlichkeit mit der im Januar eingereichten PR durchgeführt. https://github.com/aws/aws-lambda-dotnet/pull/568 Der PR hat ein vorgefertigtes Paket zum Testen beigefügt, das mit benutzerdefinierten Lambda-Laufzeiten hätte durchgeführt werden können.

Wir diskutieren den Fix hier zusammen mit der PR und ich begrüße Feedback zu dem vorgeschlagenen Fix https://github.com/aws/aws-lambda-dotnet/pull/636

Ich bin damit einverstanden, dass dies eine bahnbrechende Änderung ist, und ich bin enttäuscht, dass dies passiert, aber ich bin nicht einverstanden mit der von Ihnen vorgeschlagenen Schwere. Das Problem betrifft nur Lambda-Funktionen, die benutzerdefinierte Antworten zurückgeben, nicht alle Lambda-Funktionen und die vorhandene Amazon.Lambda.Serialization.Json-Funktion funktioniert gleich, daher glaube ich nicht, dass die gesamte Version 3.1 Lambda fehlerhaft ist. Aber ich verstehe wieder die Frustration und es tut mir leid, dass dieser Fehler vorbeigekommen ist.

Ich hoffe, die Änderungen in der PR Anfang nächster Woche voranzutreiben, es sei denn, es gibt signifikante Rückmeldungen zu den Änderungen, die zu einer Verzögerung der Veröffentlichung führen.

@bjorg Die PR wurde mit einem Link zu Vorschau2 der vorgefertigten Pakete aktualisiert. https://normj-packages.s3.us-west-2.amazonaws.com/rework-serialization-preview2.zip

@normj Mir ist klar, dass die Community hier teilweise dafür verantwortlich ist, dass diese Probleme fehlen, da (wir) Druck ausgeübt haben, die .netcore 3.1-Laufzeit als offizielles Lambda-Image freizugeben / zu unterstützen, und dies nicht gemeldet oder Feedback gegeben haben. IMHO, obwohl ich Ihre Ansicht in Bezug auf @ lukebrowell- Kommentare verstehe, würde ich teilweise @lukebrowell zustimmen und vorschlagen, eine Arbeitseinheit (unter voller Beteiligung der Community) zu starten, um eine Diskussion über Funktionen / Design der aspnetcore Lambda-Bibliothek für Funktionen / Dienste zu eröffnen im Hinblick auf die Behebung von Mängeln und / oder Fehlern, die in einem Herrenhaus festgestellt wurden, das die Entwicklung vorantreibt, da sich dieses Paket ein wenig eilig anfühlt.

Ich würde gerne eine stärkere Gemeinschaft sehen. Ich habe auf awsdevelopers.slack.com rumgehangen, aber der # dotnet-Kanal ist ein bisschen leise. Gibt es einen anderen Ort, an dem sich Lambda .NET Core-Leute versammeln?

@bjorg Ich werde Wir sehen uns dort (virtuell)

@bjorg eine Einladung bekommen?

Einladungslink von den Moderatoren erhalten. Werde es hier posten.

Ist es möglich, dieses Problem im Thema zu halten?

Einverstanden, lassen Sie uns das Thema beibehalten. Ich habe ein Community-Problem Nr. 647 erstellt, in dem erläutert wird, wie Sie mich erreichen können, um Sie zur AWS-Slack-Gruppe hinzuzufügen.

Ja, ich freue mich über Vorschläge, wie ich die Community-Kommunikation besser einrichten kann und wo ich meine eigene Kommunikation verbessern und mich stärker engagieren kann.

_preview2_ sieht gut aus für mich.

Version 2.0.0 von Amazon.Lambda.Serialization.SystemTextJson ist mit der Änderung nicht verfügbar. Main take away aktualisiert die Serializer-Klasse auf DefaultLambdaJsonSerializer .

Ich habe auch einen Blog-Beitrag veröffentlicht, der einen Abschnitt enthält, der die Änderung beschreibt.
https://aws.amazon.com/blogs/developer/one-month-update-to-net-core-3-1-lambda/

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen