Laut dem offizielle Go-Dokumentation, Um JSON-Daten zu entschlüsseln oder zu kodieren, sollten wir die Funktionen Unmarshal und Marshal verwenden. In diesem Handbuch werden daher die Begriffe „Marshalling“ und „Encoding“ synonym verwendet. 

In diesem Beitrag, Wir stellen Ihnen eine Anleitung zur Verfügung und vergleichen die beliebtesten und effektivsten schnellen Kodierungs- und Dekodierungstechniken in Go. Außerdem stellen wir Go-Codebeispiele bereit, um zu überprüfen, wie die beliebtesten Tools mit Objekten unterschiedlicher Größe umgehen.

Bibliotheken zur Beschleunigung der JSON-Ver- und Entschärfung

Es gibt mehrere Möglichkeiten, wie man JSON-Dateien mit Golang verwendet: 

Lassen Sie uns diese Pakete kurz ansehen und ein Codebeispiel für die Benchmark-Tests schreiben. 

Encoding/JSON

Golang verfügt über ein Standardpaket namens encoding/json, das eine einfache und schnelle Kodierung und Dekodierung ermöglicht. 

Hier ist ein Beispiel für einen Benchmark für das Verwalten und De-Verwalten von JSON-Objekten: 

// Benchmark-Methode für die Manöver von großen Objekten aus dem std-Paket
func BenchmarkStdMarshalLarge(b *testing.B) {
    var l int64
    für i := 0; i < b.N; i++ {
        Daten, Fehler: err := json.Marshal(&largeData)
        wenn err != nil {
            b.Error(err)
        }
        l = int64(len(data))
    }
    b.SetBytes(l)
}
 
// Benchmark-konkurrenten-Methode für die gleichzeitige Verarbeitung großer Objekte aus dem std-Paket
func BenchmarkStdMarshalLargeParallel(b *testing.B) {
    var l int64
    b.RunParallel(func(pb *testing.PB) {
        für pb.Next() {
            Daten, Fehler: err := json.Marshal(&largeData)
            wenn err != nil {
                b.Error(err)
            }
            l = int64(len(data))
        }
    })
    b.SetBytes(l)
}
 
// Benchmark-Methode für das unmarshalieren großer Objekte aus dem std-Paket
func BenchmarkStdUnmarshalLarge(b *testing.B) {
    b. SetBytes(int64(len(largeStructString)))
    für i := 0; i < b.N; i++ {
        var s LargeStruct
        err := json.Unmarshal(largeStructString, &s)
        wenn err != nil {
            b.Error(err)
        }
    }
}

Dieses Paket verwendet jedoch eine Reflexion, während es gleichzeitig das Mitglied einer Struktur deklariert und deren Typ definiert. Dies führt zu geringer Leistung bei Systemen mit hohem Lastaufwand. Die Binärkodierung ist die beste Methode, um dieses Problem zu lösen. Sie ist in der Standardbibliothek nicht enthalten, wird jedoch von anderen Go-Bibliotheken wie ffjson und easyjson weit verbreitet verwendet.  

ffjson

Der Hauptzweck dieses Pakets ist es, die JSON-Seriellisierung zu erleichtern, ohne dass zusätzliche Codeänderungen erforderlich sind. Ffjson generiert statische MarshalJSON und UnmarshalJSON Funktionen, die die Abhängigkeit von der Laufzeitreflektion für die Serialisierung reduzieren. 

Wenn ffjson ein involviertes Typenobjekt nicht versteht, greift es auf encoding/json zurück. Das bedeutet, dass das Paket eine sichere Alternative darstellt. 

Um Code zu generieren, fügen Sie die folgende Zeile in Ihre Datei ein: 

ffjson .go

Hier ist ein Beispiel für statisch generiertes Code für die Ver- und Entmachemethoden mit dem ffjson-Paket:

// MarshalJSON-Bytes in JSON umwandeln – Vorlage
Funktion (j *LargeStruct) MarshalJSON() ([]Byte, Fehler) {
    var buf fflib.Buffer
    wenn j == nil {
        buf.WriteString("null")
        zurück buf.Bytes(), nil
    }
    err := j.MarshalJSONBuf(&buf)
    wenn err != nil {
        zurück null, Fehlschlag
    }
    zurück buf.Bytes(), nil
}
 
...
 
// UnmarshalJSON umarshall json - ffjson-Vorlage
func (j *LargeStruct) UnmarshalJSON(input []Byte) Fehler {
    fs := fflib.NewFFLexer(input)
    zurück j.UnmarshalJSONFFLexer(fs, fflib.FFParse_map_start)
}

Und hier ein Beispiel für die Methoden marshallen/unmarshallen für große JSON-Objekte: 

// Benchmark-Methode für die Manöver von großen Objekten aus dem ffjson-Paket
func BenchmarkFfJsonMarshalLarge(b *testing.B) {
    var l int64
    für i := 0; i < b.N; i++ {
        Daten, Fehler: err := ffjson.MarshalFast(&largeData)
        wenn err != nil {
            b.Error(err)
        }
        l = int64(len(data))
    }
    b.SetBytes(l)
}
 
// Benchmark-Methode für den Marshall von großen Objekten mit einem Pool aus dem ffjson-Paket
func BenchmarkFfJsonMarshalLargeWithPool(b *testing.B) {
    var l int64
    für i := 0; i < b.N; i++ {
        Daten, Fehler: err := ffjson.MarshalFast(&largeData)
        wenn err != nil {
            b.Error(err)
        }
        l = int64(len(data))
        ffjson.Pool(data)
    }
    b.SetBytes(l)
}
 
// Benchmark-konkurrenten-Methode für die massive Objekt-Verwalterung aus dem ffjson-Paket
func BenchmarkFfJsonMarshalLargeParallel(b *testing.B) {
    var l int64
    b.RunParallel(func(pb *testing.PB) {
        für pb.Next() {
            Daten, Fehler: err := ffjson.MarshalFast(&largeData)
            wenn err != nil {
                b.Error(err)
            }
            l = int64(len(data))
        }
    })
    b.SetBytes(l)
}
 
// Benchmark-Methode zur gleichzeitigen Verarbeitung großer Objekte mit einem Pool aus dem Paket ffjson
func BenchmarkFfJsonMarshalLargeWithPoolParallel(b *testing.B) {
    var l int64
    b.RunParallel(func(pb *testing.PB) {
        für pb.Next() {
            Daten, Fehler: err := ffjson.MarshalFast(&largeData)
            wenn err != nil {
                b.Error(err)
            }
            l = int64(len(data))
            ffjson.Pool(data)
        }
    })
    b.SetBytes(l)
}
 
// Benchmark-Methode für das unmarshalieren großer Objekte aus dem ffjson-Paket
func BenchmarkFfJsonUnmarshalLarge(b *testing.B) {
    b. SetBytes(int64(len(largeStructString)))
    für i := 0; i < b.N; i++ {
        var s LargeStruct
        wenn err := ffjson.UnmarshalFast(largeStructString, &s); err != nil {
            b.Error(err)
        }
    }
}

easyjson

Ziel dieses Pakets ist es, den generierten Go-Code so einfach zu halten, dass er leicht optimiert oder korrigiert werden kann. Ein weiteres Ziel ist es, den Benutzern die Möglichkeit zu geben, den generierten Code anzupassen, indem Optionen bereitgestellt werden, die im Standard-Encoding-/JSON-Paket nicht verfügbar sind.

Fügen Sie diese Zeile hinzu, um Code zu generieren:

easyjson -all .go

Including -all generiert einen Marshaller und ein Unmarshaller für alle Go-Strukturen in der .go Datei. Hier ist ein Beispiel für statisch generiertes Code: 

// MarshalJSON unterstützt die json.Marshaler-Implementierung
Funktion (v LargeStruct) MarshalJSON() ([]Byte, Fehler) {
    w := jwriter.Writer{}
    easyjson794297d0EncodeGitlabYalantisComJsonEncodingBenchmarkEasyjson10(&w, v)
    zurück w.Buffer.BuildBytes(), w.Error
}
 
// UnmarshalJSON unterstützt die json.Unmarshaler-Schnittstelle
func (v *LargeStruct) UnmarshalJSON(data []Byte) Fehler {
    r := jlexer.Lexer(Data: data)
    easyjson794297d0DecodeGitlabYalantisComJsonEncodingBenchmarkEasyjson10(&r, v)
    zurück r.Error()
}

Und hier sind Beispiele für Benchmark-Methoden zur Strukturierung und Defragmentierung großer JSON-Objekte. 

 // Benchmark-Methode für die Manöver von großen Objekten aus dem Paket easyjson
func BenchmarkEasyJsonMarshalLarge(b *testing.B) {
    var l int64
    für i := 0; i < b.N; i++ {
        Daten, Fehler: err := largeData.MarshalJSON()
        wenn err != nil {
            b.Error(err)
        }
        l = int64(len(data))
    }
    b.SetBytes(l)
}
 
// Benchmark-Methode für die gleichzeitige Verarbeitung großer Objekte aus dem Paket easyjson
func BenchmarkEasyJsonMarshalLargeParallel(b *testing.B) {
    b.SetBytes(int64(len(largeStructString)))
    b.RunParallel(func(pb *testing.PB) {
        für pb.Next() {
            wenn _, err := largeData.MarshalJSON(); err != nil {
                b.Error(err)
            }
        }
    })
}
 
// Benchmark-Methode für das unmarshalieren großer Objekte aus dem EasyJSON-Paket
func BenchmarkEasyJsonUnmarshallLarge(b *testing.B) {
    b. SetBytes(int64(len(largeStructString)))
    für i := 0; i < b.N; i++ {
        var s LargeStruct
        wenn err := s.UnmarshalJSON(largeStructString); err != nil {
            b.Error(err)
        }
    }
}

Die oben genannten Pakete implementieren binäre Codekompressoren, die statische Code für jede Komponente generieren. Dadurch wird die Serialisierung beschleunigt. 

fastjson

Eine weitere nützliche Technik, die das Kodieren und Entkodieren erleichtert, ist die direkte String-Splitting. Die folgende Methode implementiert weder das Marshalling noch das Unmarshalling. Sie übernimmt lediglich die Funktionen zur Arbeit mit String-Variablen im JSON-Format und stellt sich als gute Lösung für das Marshalling heraus. 

Dieses Paket analysiert beliebiges JSON-Format ohne Code-Generierung, Schema und Reflexion. Es extrahiert schnell einen Teil des ursprünglichen JSON-Formats mit Value.Get(...).MarshalTo und modifiziert es mit dem Del und Set Funktionen. Es kann Arrays mit Werten unterschiedlicher Typen analysieren – zum Beispiel kann es die folgende JSON-Array-Ausführung problemlos analysieren: [458, "foo", [155], {"a": "b"}, null].

Hier ist ein Beispiel für ein Benchmark-Tool zur Analyse großer Objekte:  

// Benchmark-Methode für die Analyse großer Objekte aus dem FastJson-Paket
func BenchmarkFastJsonParseLarge(b *testing.B) {
    b. SetBytes(int64(len(xlStructString)))
    für i := 0; i < b.N; i++ {
        wenn _, err := fastjson.Parse(String(largeStructString)); err != nil {
            b.Error(err)
        }
    }
}

Aber es ist nicht in der Lage, JSON aus io.Reader zu entziffern. Um die JSON-Dekodierung in Golang von einer Zeichenkette zu beschleunigen, sollten Sie den Scanner-Typ verwenden.

json-iterator/go

Wie das Standardpaket basiert auch dieses auf Reflexionen, bietet jedoch bessere Leistung und Geschwindigkeit.

Es erfordert keine Codegenerierung; einfach importieren json-iterator/go Anstelle des Standardpakets. Hier finden Sie ein Beispiel für eine Benchmarkmethode zur Strukturierung und Defragmentierung großer JSON-Objekte. 

// Benchmark-Methode für die Manöver von großen Objekten aus dem Paket jsoniter
func BenchmarkJsonIterMarshalLarge(b *testing.B) {
    var l int64
    für i := 0; i < b.N; i++ {
        Daten, Fehler: err := jsoniter.Marshal(&largeData)
        wenn err != nil {
            b.Error(err)
        }
        l = int64(len(data))
    }
    b.SetBytes(l)
}
 
// Benchmark-Methode für die gleichzeitige Verarbeitung großer Objekte aus dem Paket jsoniter
func BenchmarkJsonIterMarshalLargeParallel(b *testing.B) {
    var l int64
    b.RunParallel(func(pb *testing.PB) {
        für pb.Next() {
            Daten, Fehler: err := jsoniter.Marshal(&largeData)
            wenn err != nil {
                b.Error(err)
            }
            l = int64(len(data))
        }
    })
    b.SetBytes(l)
}
 
// Benchmark-Methode für das unmarshalieren großer Objekte aus dem Paket jsoniter
func BenchmarkJsonIterUnmarshalLarge(b *testing.B) {
    b. SetBytes(int64(len(largeStructString)))
    für i := 0; i < b.N; i++ {
        var s LargeStruct
        err := jsoniter.Unmarshal(largeStructString, &s)
        wenn err != nil {
            b.Error(err)
        }
    }
}

Interessiert, wie viel schneller Ihre Anwendung sein könnte? Vergleichen wir Ihre Anwendung und finden die beste JSON-Bibliothek für den Einsatz.

Erhalten Sie eine Performanceanalyse

Bibliotheken vergleichen

Zunächst einmal führen wir eine allgemeine Analyse der Pakete durch. Sowohl ffjson als auch easyjson werden in gleichem Tempo entwickelt und haben keine offizielle Veröffentlichung. Json-iterator/go wird intensiv entwickelt, und seine Entwickler lösen Probleme schnell. Außerdem hat dieses Paket schnell starke Unterstützung in der Entwicklergemeinschaft gewonnen. 

Trotz seines jungen Alters hat valyala/fastjson in der Entwicklercommunity Vertrauen gewonnen. Bislang gibt es eine kleine Liste von Problemen, von denen die meisten schnell behoben wurden.

Die folgende Tabelle fasst die allgemeinen Eigenschaften dieser Pakete zusammen. Sie listet die aktuellen Versionen der Pakete oder deren letzten gemeinsamen Hash-Werte auf, falls das Paket keine offizielle Veröffentlichung hat. 

Der nächste Schritt unserer Vergleichsarbeit ist die Geschwindigkeitsprüfung. Dafür verwendeten wir Go 1.12.6. Mit dem Code oben prüfen wir, wie schnell das Standardpaket und die vier alternativen Lösungen die Go-JSON-Dekodierung und -Encoding durchführen. 

Lesen Sie auch: Node.js vs Go: Was ist besser für die Backend-Webentwicklung?

Für präzisere Ergebnisse haben wir Benchmark-Tests mit drei Arten von Objekten durchgeführt:

  • Kleinere Objekte (bis zu 512 KB)
  • Große Dateien (ab 1 bis 10 MB)
  • Extrem große Dateien (größer als 10 MB).

Hier sind die Ergebnisse unserer Benchmark-Tests:

Um die Ergebnisse der Benchmarktests zu analysieren, haben wir einen Balkendiagramm erstellt, das die Geschwindigkeit der Kodierung und Dekodierung von JSON-Objekten sowie die Speicherplatzanforderungen für die Pakete encoding/json, json-iterator/go, ffjson und easyjson anzeigt. 

Hier sind die Indikatoren für die Speicherzuweisung:

Hier versuchen wir, den schnellsten JSON-Encoder/Decoder zu definieren: 

Vergleichen wir die Pakete, die für die Kodierung/Dekodierung die Reflection-Technologie verwenden: encoding/json und json-iterator/go. Das Standardpaket führt die Kodierung schneller durch. Wenn wir über die Beschleunigung des Dekodierscanners sprechen, führt json-iterator/go vier Mal schneller als die Standardbibliothek.

Anschließend folgen die Programme easyjson und ffjson, die statische Codegenerierung verwenden. Die Benchmarktests zeigten, dass easyjson sowohl bei der Kodierung als auch bei der Dekodierung 1,5 bis 3 Mal schneller als fastjson arbeitet. Die Ergebnisse zeigten außerdem eine dreimal schnellere parallele Kodierung bzw. Dekodierung im Vergleich zu anderen Paketen. 

Solche hohen Ergebnisse werden durch die effektive Nutzung eines Pufferpools erzielt, der große Datenblöcke in kleinere Teile aufteilt, um sie anschließend zu nutzen. sync.Pool(). Diese Leistungsverbesserungen sind besonders vorteilhaft für Unternehmen, die Erweiterte Datenwissenschaftsdienste, wo die effiziente Verarbeitung großer Datensätze entscheidend für die Bereitstellung schneller und präziser Erkenntnisse ist.

Die Ergebnisse von fastjson zeigten, dass die Parsing-Methode bei kleinen Objekten 3.600 Mal schneller funktioniert als bei anderen Paketen. Andererseits nimmt ihre Geschwindigkeit jedoch ab, je größer das Objekt ist. Die Geschwindigkeit der Kodierung großer Objekte ist also 2 bis 3 Mal langsamer als bei anderen Paketen. Dies wird durch den zeitaufwändigen Prozess der Textanalyse erklärt. 

Fazit 

Unsere Benchmark-Tests lassen uns die folgenden Schlussfolgerungen ziehen:

  • Encoding/json ist eine gute Lösung für die Arbeit mit kleinen Objekten, die keine hohen Belastungen vertragen müssen. 
  • Wenn Sie Millionen Objekte mit einer ähnlichen Struktur verwalten müssen, können Sie Pakete mit statischer Codegenerierung verwenden. Die Binärserialization erfordert im Vergleich zu anderen Methoden doppelt bis viermal mehr RAM für die Datenverarbeitung.
  • Die in fastjson verwendete Parsing-Methode bietet außergewöhnliche Performance, kann jedoch keine JSON-Daten in Objekte umwandeln; stattdessen erstellt sie einfach ein Feldset. Sie können auf diese Felder mit ihren Schlüsseln zugreifen. Das fastjson-Paket kann verwendet werden, wenn Sie prüfen möchten, ob ein Feld existiert oder die Wert erhalten möchten, ohne den Dekodierungsprozess durchlaufen zu müssen. 

Wie Sie sehen können, hängt die Wahl der Methoden für die Verteilung und Entfaltung der Daten stark vom Typ der Daten ab, mit denen Sie arbeiten möchten. Wenn Sie sich fragen, welche Lösung für Ihr Projekt am besten geeignet ist oder Entwickler für Go in Ihrem Projekt suchen möchten, können Sie jederzeit Schreiben Sie uns. Gerne helfen wir Ihnen bei der Entwicklung in Golang. 

Wollen Sie die besten IT-Talente einstellen, um Ihr Entwicklerteam zu stärken?

Überlegen Sie die Verstärkung des Teams mit Yalantis

Erfahren Sie, was wir zu bieten haben

Über den Autor

Foto von Nataliia Horbei

Inhaltmanager

Mit mehr als sechs Jahren Erfahrung in der Softwareentwicklung konzentriert sich Nataliia auf die Erstellung eingehender Inhalte zu Themen wie Web- und Mobile-Entwicklung, IoT, KI/ML und Cloud-Lösungen. Ihre Arbeit umfasst mehrere Branchen, von der Gesundheitsversorgung und Fintech über Logistik und Immobilienwirtschaft bis hin zu Finanzdienstleistungen.