Yaml: Los tipos compuestos no se deshacen correctamente (es decir, el comportamiento difiere de json.Unmarshal)

Creado en 22 dic. 2014  ·  7Comentarios  ·  Fuente: go-yaml/yaml

Parece que yaml.Unmarshal() y json.Unmarshal() manejan los tipos compuestos de manera diferente. El desagrupamiento de yaml no parece leer las etiquetas en el tipo interno. Tenga en cuenta que ValueA se ordena correctamente en el caso json, pero no en el caso yaml.

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)
}

Comentario más útil

Esto funciona:

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)
}

Todos 7 comentarios

Esto funciona:

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)
}

(Acabo de agregar una etiqueta en el campo A anónimo). Si esto es "buen comportamiento" o no, depende. Estoy de acuerdo en que, en general, go-yaml debería funcionar de manera similar a go-json. En este caso, sin embargo, no me gusta mucho la forma en que funciona go-json, simplemente parece demasiado "mágico".

Dicho esto, agregar yaml:",inline" tampoco es una buena solución.

Pensándolo bien, quizás establecer campos anónimos para que estén automáticamente "en línea" tenga sentido.

De hecho, esto es inconsistente con el paquete json estándar, y la razón principal por la que es inconsistente es porque se implementó antes de que el paquete json admitiera esa característica.

Dicho esto, para ser honesto, prefiero la forma en que funciona en el paquete yaml. Esto permite mantener el hecho de si los campos del tipo B en su ejemplo deben ser exportados o no ortogonales a si los quiere en línea en la salida de yaml.

Por ejemplo, esto funciona:

foo Foo `yaml:",inline"`

Inserta correctamente el campo en la salida de yaml, a pesar de que en Go este no es un campo anónimo.

En cualquier caso, todo esto es teoría y trasfondo. No vamos a cambiar esto ahora porque rompería el código existente de una manera difícil de anticipar, lo cual es realmente malo para un paquete. Espero que podamos estar de acuerdo en eso, ya sea que considere este comportamiento una característica o una verruga.

La cuestión de la compatibilidad con versiones anteriores es sin duda una consideración importante. Gracias por la explicación.

(Aparte de tangencial no relacionado directamente con go-yaml: desearía que Go tuviera un mecanismo de control de versiones de paquetes estandarizado para que un cambio incompatible hacia atrás como este pudiera considerarse y aún implementarse sin problemas sin interferir inesperadamente con los usuarios existentes de la biblioteca).

@niemeyer Gracias por tu trabajo en esta biblioteca, por cierto. Es muy apreciado.

@bcronin : go-yaml usa pkg.in para ofrecer el control de versiones de paquetes estandarizado del que habla.

Notarás que la forma correcta de importar la biblioteca es:

import "gopkg.in/yaml.v2"

lo que significa - versión 2. Sería muy fácil crear una nueva rama, llamémosla - v3, y romper la compatibilidad con versiones anteriores allí. Nadie esperaría que funcione como v2, al igual que nadie espera que v2 funcione exactamente como v1.

Entonces, aunque estoy de acuerdo con @niemeyer en que el comportamiento existente es mejor (en mi opinión), no hago su comentario sobre romper la compatibilidad con versiones anteriores. Solo que este cambio probablemente no justifique una v3 por sí solo.

¡Gracias por las ideas! Por mi parte, puedo decir que esta característica / error me sorprendió y terminé pasando aproximadamente una hora antes de llegar a este problema de Github. Dado que este es un comportamiento importante y sorprendente, ¿puedo preguntar si lo destacamos en el archivo Léame?

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

mro picture mro  ·  13Comentarios

thallgren picture thallgren  ·  8Comentarios

lgo picture lgo  ·  5Comentarios

nicksnyder picture nicksnyder  ·  4Comentarios

johscheuer picture johscheuer  ·  13Comentarios