Yaml: Zusammengesetzte Typen werden nicht richtig demarshallt (dh das Verhalten unterscheidet sich von json.Unmarshal)

Erstellt am 22. Dez. 2014  ·  7Kommentare  ·  Quelle: go-yaml/yaml

Sieht so aus, als würden yaml.Unmarshal() und json.Unmarshal() zusammengesetzte Typen unterschiedlich handhaben. Das Unmarshalling von yaml scheint die Tags des inneren Typs nicht zu lesen. Hinweis ValueA wird im JSON-Fall korrekt demarshallt, aber nicht im yaml-Fall.

package main

import (
    "encoding/json"
    "fmt"

    "github.com/go-yaml/yaml"
)

type A struct {
    ValueA int `json:"a" yaml:"a"`
}
type B struct {
    A
    ValueB int `json:"b" yaml:"b"`
}

const jsonData = `{ "a" : 1, "b" : 2 }`
const yamlData = `
a : 1
b : 2
`

func main() {
    jdata := B{}
    ydata := B{}

    json.Unmarshal([]byte(jsonData), &jdata)
    yaml.Unmarshal([]byte(yamlData), &ydata)

    // $ go run main.go
    // json: {{1} 2}
    // yaml: {{0} 2}
    fmt.Printf("json: %v\n", jdata)
    fmt.Printf("yaml: %v\n", ydata)
}

Hilfreichster Kommentar

Das funktioniert:

package main

import (
    "encoding/json"
    "fmt"

    "gopkg.in/yaml.v2"
)

type A struct {
    ValueA int `json:"a" yaml:"a"`
}
type B struct {
    A      `yaml:",inline"`
    ValueB int `json:"b" yaml:"b"`
}

const jsonData = `{ "a" : 1, "b" : 2 }`
const yamlData = `
a : 1
b : 2
`

func main() {
    jdata := B{}
    ydata := B{}

    json.Unmarshal([]byte(jsonData), &jdata)
    yaml.Unmarshal([]byte(yamlData), &ydata)

    // $ go run main.go
    // json: {{1} 2}
    // yaml: {{1} 2}
    fmt.Printf("json: %v\n", jdata)
    fmt.Printf("yaml: %v\n", ydata)
}

Alle 7 Kommentare

Das funktioniert:

package main

import (
    "encoding/json"
    "fmt"

    "gopkg.in/yaml.v2"
)

type A struct {
    ValueA int `json:"a" yaml:"a"`
}
type B struct {
    A      `yaml:",inline"`
    ValueB int `json:"b" yaml:"b"`
}

const jsonData = `{ "a" : 1, "b" : 2 }`
const yamlData = `
a : 1
b : 2
`

func main() {
    jdata := B{}
    ydata := B{}

    json.Unmarshal([]byte(jsonData), &jdata)
    yaml.Unmarshal([]byte(yamlData), &ydata)

    // $ go run main.go
    // json: {{1} 2}
    // yaml: {{1} 2}
    fmt.Printf("json: %v\n", jdata)
    fmt.Printf("yaml: %v\n", ydata)
}

(Ich habe gerade ein Tag im anonymen Feld A hinzugefügt.) Ob dies "gutes Benehmen" ist oder nicht, hängt davon ab. Ich stimme zu, dass go-yaml im Allgemeinen ähnlich wie go-json funktionieren sollte. In diesem Fall gefällt mir die Funktionsweise von go-json jedoch nicht wirklich - es scheint einfach zu "magisch".

Davon abgesehen ist das Hinzufügen von yaml:",inline" keine schöne Lösung.

Auf den zweiten Blick macht es vielleicht Sinn, anonyme Felder automatisch "inline" zu setzen.

Dies ist in der Tat inkonsistent mit dem Standard-Json-Paket, und der Hauptgrund für die Inkonsistenz ist, dass es implementiert wurde, bevor das Json-Paket diese Funktion unterstützte.

Um ehrlich zu sein, bevorzuge ich jedoch die Art und Weise, wie es im yaml-Paket funktioniert. Dies ermöglicht es, die Tatsache beizubehalten, ob die Felder des Typs B in Ihrem Beispiel exportiert werden sollen oder nicht, orthogonal dazu, ob sie in die yaml-Ausgabe integriert werden sollen.

Das funktioniert zum Beispiel:

foo Foo `yaml:",inline"`

Es fügt das Feld korrekt in die yaml-Ausgabe ein, obwohl dies in Go kein anonymes Feld ist.

In jedem Fall ist dies alles Theorie und Hintergrund. Wir ändern dies jetzt nicht, weil dies den existierenden Code auf schwer vorhersehbare Weise zerstören würde, was für ein Paket wirklich schlecht ist. Ich hoffe, wir können uns darin einigen, egal ob Sie dieses Verhalten als Feature oder Warze betrachten.

Das Problem der Abwärtskompatibilität ist sicherlich ein wichtiger Aspekt. Danke für die Erklärung.

(Tangentiale Nebensache, die nicht direkt mit go-yaml zu tun hat: Ich wünschte, Go hätte einen standardisierten Mechanismus zur Paketversionierung, damit eine solche abwärtsinkompatible Änderung berücksichtigt und dennoch reibungslos ausgerollt werden könnte, ohne die bestehenden Benutzer der Bibliothek unerwartet zu stören.)

@niemeyer Danke

@bcronin - go-yaml verwendet pkg.in, um die standardisierte Paketversionierung anzubieten, von der Sie sprechen.

Sie werden feststellen, dass der richtige Weg zum Importieren der Bibliothek ist:

import "gopkg.in/yaml.v2"

was bedeutet - Version 2. Es wäre sehr einfach, einen neuen Zweig zu erstellen, nennen wir ihn - v3, und dort die Abwärtskompatibilität aufzuheben. Niemand würde erwarten, dass es wie v2 funktioniert, genauso wie niemand erwartet, dass v2 genau wie v1 funktioniert.

Also, obwohl ich @niemeyer zustimme,

Danke für die Einblicke! Ich für meinen Teil kann sagen, dass mich diese Funktion / dieses Fehlfeature überrascht hat und ich am Ende ungefähr eine Stunde damit verbracht habe, bis ich dieses Github-Problem erreichte. Da dies ein wichtiges und überraschendes Verhalten ist, darf ich fragen, ob wir dies in der Readme-Datei hervorheben?

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen