Go 1.25 encoding/json v1 vs v2 Vergleich
Die encoding/json
-Paket v2 von Go ist eine neue Implementierung, die darauf abzielt, verschiedene Mängel der bestehenden v1 (mangelnde Konsistenz, überraschendes Verhalten, Leistungsprobleme) zu verbessern. Es handelt sich um eine experimentelle Funktion, die über den Build-Tag goexperiment.jsonv2
aktiviert wird.
Am wichtigsten ist, dass v1, wenn v2 aktiviert ist, als Kompatibilitätsschicht fungiert, die das Verhalten von v1 über der v2-Implementierung emuliert. Dies wird durch die Funktion DefaultOptionsV1()
in der Datei v2_v1.go
erreicht. Das bedeutet, v2 bietet Optionen, um das Verhalten von v1 vollständig zu reproduzieren, während es gleichzeitig ein neues, strengeres und vorhersagbareres Standardverhalten einführt.
Die Hauptziele von v2 sind:
- Verbesserung der Genauigkeit und Vorhersagbarkeit: Durch die Anwendung strengerer Regeln (z. B. Groß-/Kleinschreibung, Verbot doppelter Schlüssel) werden unerwartete Verhaltensweisen reduziert.
- Leistungsverbesserung: Das Parsing- und Encoding-Engine wurde neu gestaltet, um die Effizienz zu steigern.
- Erweiterte Flexibilität und Kontrolle: Ein detailliertes Optionssystem wurde eingeführt, das Entwicklern eine feine Kontrolle über die JSON-Verarbeitung ermöglicht.
Wesentliche Bedeutungs-/Verhaltensunterschiede
Die Verhaltensunterschiede zwischen v1 und v2 wurden, hauptsächlich basierend auf der Datei v2_test.go
, nach Elementen geordnet.
1. Feldnamen-Matching (Groß-/Kleinschreibung)
- v1-Verhalten: Beim Unmarshalling von JSON-Objektmitgliedern in Go-Strukturfelder erfolgt das Matching ohne Berücksichtigung der Groß-/Kleinschreibung (case-insensitive). Sowohl
"FirstName"
als auch"firstname"
werden dem FeldFirstName
zugeordnet. - v2-Verhalten: Standardmäßig erfolgt das Matching unter Berücksichtigung der Groß-/Kleinschreibung (case-sensitive), wobei nur exakt übereinstimmende Felder zugeordnet werden.
- Grund der Änderung: Das Matching ohne Berücksichtigung der Groß-/Kleinschreibung kann zu unerwartetem Verhalten führen und eine Leistungseinbuße verursachen, wenn nicht übereinstimmende Felder verarbeitet werden. v2 hat ein klareres und vorhersagbareres Verhalten als Standard übernommen.
- Verwandte Optionen: In v2 kann die Groß-/Kleinschreibung pro Feld explizit über die Tag-Option
json:"...,case:ignore"
aktiviert oder die Optionjson.MatchCaseInsensitiveNames(true)
global angewendet werden.
2. Geänderte Bedeutung der omitempty
-Tag-Option
- v1-Verhalten: Felder werden basierend auf dem "leeren Zustand" des Go-Werts weggelassen. "Leerer Zustand" bedeutet hier
false
,0
,nil
-Pointer/Interface, oder Arrays/Slices/Maps/Strings der Länge 0. - v2-Verhalten: Felder werden basierend auf dem "leeren Zustand" des enkodierten JSON-Werts weggelassen. Das heißt, sie werden weggelassen, wenn sie als
null
,""
,{}
,[]
enkodiert werden. - Grund der Änderung: Die Definition von v1 ist an das Typensystem von Go gebunden. v2 bietet ein konsistenteres Verhalten, basierend auf dem JSON-Typensystem. Zum Beispiel wird in v1 der
false
-Wert einesbool
-Typs weggelassen, während in v2false
kein leerer JSON-Wert ist und daher nicht weggelassen wird. In v2 kann die Optionomitzero
hinzugefügt werden, um das Verhalten von v1 zu ersetzen, bei demomitempty
auf0
oderfalse
angewendet wurde. - Verwandte Optionen: Wenn in v2 das gleiche Verhalten wie in v1 gewünscht wird, verwenden Sie die Option
json.OmitEmptyWithLegacyDefinition(true)
.
3. Geändertes Verhalten der string
-Tag-Option
- v1-Verhalten: Wird auf numerische, boolesche und String-Typ-Felder angewendet. Der Wert wird erneut in einen JSON-String enkodiert (z. B.
int(42)
->"42"
). Werte innerhalb komplexer Typen (Slices, Maps usw.) werden nicht rekursiv angewendet. - v2-Verhalten: Wird nur auf numerische Typen angewendet und rekursiv. Das heißt, auch Zahlen innerhalb von Slices wie
[]int
werden alle als JSON-Strings enkodiert. - Grund der Änderung: Der Hauptzweck der
string
-Option ist die Darstellung von Zahlen als Strings, um den Verlust der Präzision bei 64-Bit-Ganzzahlen zu verhindern. Das Verhalten von v1 war begrenzt und inkonsistent. v2 konzentriert sich auf diesen Kernzweck und erweitert das Verhalten rekursiv, um es nützlicher zu machen. - Verwandte Optionen: Das Verhalten von v1 kann mit der Option
json.StringifyWithLegacySemantics(true)
nachgeahmt werden.
4. Marshalling von nil
-Slices und -Maps
- v1-Verhalten:
nil
-Slices undnil
-Maps werden alsnull
gemarshallt. - v2-Verhalten: Standardmäßig werden
nil
-Slices als[]
(leeres Array) undnil
-Maps als{}
(leeres Objekt) gemarshallt. - Grund der Änderung:
nil
ist ein Implementierungsdetail der Go-Sprache, und es ist nicht wünschenswert, es in ein sprachunabhängiges JSON-Format zu exportieren.[]
oder{}
sind gebräuchlichere Darstellungen für leere Kollektionen. - Verwandte Optionen: In v2 kann durch die Optionen
json.FormatNilSliceAsNull(true)
oderjson.FormatNilMapAsNull(true)
das Marshalling alsnull
wie in v1 erfolgen.
5. Array-Unmarshalling
- v1-Verhalten: Beim Unmarshalling in Go-Arrays (
[N]T
) wird kein Fehler ausgelöst, auch wenn die Länge des JSON-Arrays von der Länge des Go-Arrays abweicht. Wenn die Länge kürzer ist, werden die verbleibenden Felder mit Nullwerten gefüllt; wenn sie länger ist, werden die überschüssigen Werte verworfen. - v2-Verhalten: Die Länge des JSON-Arrays muss exakt mit der Länge des Go-Arrays übereinstimmen. Andernfalls wird ein Fehler ausgelöst.
- Grund der Änderung: In Go haben Arrays fester Größe oft eine wichtige Bedeutung hinsichtlich ihrer Länge. Das Verhalten von v1 konnte zu einem stillen Datenverlust führen. v2 erhöht die Genauigkeit durch strengere Regeln.
- Verwandte Optionen: Das Verhalten von v1 kann mit der Option
json.UnmarshalArrayFromAnyLength(true)
nachgeahmt werden.
6. time.Duration
-Verarbeitung
- v1-Verhalten:
time.Duration
wird intern alsint64
behandelt und als JSON-Zahl in Nanosekunden enkodiert. - v2-Verhalten: Wird unter Verwendung der
time.Duration.String()
-Methode als JSON-String im Format"1h2m3s"
enkodiert. - Grund der Änderung: Numerische Nanosekunden sind schlecht lesbar, und die Standard-String-Darstellung von
time.Duration
ist nützlicher. - Verwandte Optionen: Das Verhalten von v1 kann über die Tag-Option
json:",format:nano"
oder die Optionjson.FormatTimeWithLegacySemantics(true)
verwendet werden.
7. Umgang mit ungültigem UTF-8
- v1-Verhalten: Beim Marshalling/Unmarshalling werden ungültige UTF-8-Bytes in einem String stillschweigend durch das Unicode-Ersatzzeichen (
\uFFFD
) ersetzt. - v2-Verhalten: Standardmäßig wird ein Fehler zurückgegeben, wenn ungültiges UTF-8 angetroffen wird.
- Grund der Änderung: Dies dient dazu, stillschweigende Datenkorruption zu verhindern und dem strengeren JSON-Standard (RFC 7493) zu entsprechen.
- Verwandte Optionen: Das Verhalten von v1 kann mit der Option
jsontext.AllowInvalidUTF8(true)
nachgeahmt werden.
8. Umgang mit doppelten Objektnamen
- v1-Verhalten: Erlaubt das doppelte Auftreten von Membern mit demselben Namen innerhalb eines JSON-Objekts. Der zuletzt erschienene Wert überschreibt frühere.
- v2-Verhalten: Standardmäßig wird ein Fehler zurückgegeben, wenn doppelte Member-Namen vorhanden sind.
- Grund der Änderung: Der RFC 8259-Standard definiert das Verhalten bei doppelten Namen nicht, was zu unterschiedlichem Verhalten je nach Implementierung führen kann. Dies kann eine Ursache für Sicherheitslücken sein. v2 lehnt dies explizit ab, um die Genauigkeit und Sicherheit zu erhöhen.
- Verwandte Optionen: Das Verhalten von v1 kann mit der Option
jsontext.AllowDuplicateNames(true)
nachgeahmt werden.
Implementierungs- und Architekturunterschiede
- v1: Stark abhängig von
decodeState
indecode.go
und einer manuell geschriebenen State Machine inscanner.go
. Dies ist eine monolithische Struktur, bei der die Parsing-Logik und die semantische Analyse stark gekoppelt sind. - v2: Die Architektur ist modularer aufgebaut.
encoding/json/jsontext
: Bietet einen Low-Level-High-Performance-JSON-Tokenisierer (Decoder
) und -Encoder (Encoder
). Dieses Paket konzentriert sich ausschließlich auf die syntaktischen Aspekte von JSON.encoding/json/v2
: Basiert aufjsontext
und behandelt die semantische Konvertierung zwischen Go-Typen und JSON-Werten.- Diese Trennung von Syntax- und Semantikanalyse verbessert die Codeklarheit und Leistung.
Neue API und Funktionen in v2
v2 bietet durch das json.Options
-System eine sehr flexible Steuerungsfunktion.
json.Options
: Eine Sammlung von Optionen, die das Marshalling-/Unmarshalling-Verhalten ändern.json.JoinOptions(...)
: Fasst mehrere Optionen zu einer zusammen.WithMarshalers
/WithUnmarshalers
: Eine leistungsstarke Funktion, die das Injizieren von Serialisierungs-/Deserialisierungslogik für bestimmte Typen ermöglicht, ohne dass dieMarshaler
-/Unmarshaler
-Interfaces implementiert werden müssen. Dies ist besonders nützlich bei der Verarbeitung von Typen aus externen Paketen.- Neue Optionen:
RejectUnknownMembers
,Deterministic(false)
,FormatNilSliceAsNull
und viele andere Verhaltenssteuerungen, die in v1 nicht möglich waren, sind nun verfügbar.
Fazit
encoding/json
v2 ist eine moderne Implementierung, die auf den Erfahrungen von v1 aufbaut und Genauigkeit, Leistung und Flexibilität erheblich verbessert. Obwohl das Standardverhalten strenger geworden ist, unterstützt das ausgefeilte Options
-System alle Verhaltensweisen von v1 vollständig, sodass die Vorteile von v2 schrittweise eingeführt werden können, während die Kompatibilität mit bestehendem Code erhalten bleibt.
- Für neue Projekte wird empfohlen, v2 standardmäßig zu verwenden.
- Bestehende Projekte können entweder
jsonv1
weiterhin verwenden oder eine Strategie verfolgen, bei der sie zujsonv2
migrieren und das strengere Verhalten von v2 schrittweise überDefaultOptionsV1()
einführen.