handling - json to interface golang
¿Cómo no ordenar una estructura vacía en JSON con Go? (3)
Tengo una estructura como esta:
type Result struct {
Data MyStruct `json:"data,omitempty"`
Status string `json:"status,omitempty"`
Reason string `json:"reason,omitempty"`
}
Pero incluso si la instancia de MyStruct está completamente vacía (es decir, todos los valores son predeterminados), se está serializando como:
"data":{}
Sé que los documentos de encoding/json especifican que los campos "vacíos" son:
falso, 0, cualquier puntero nulo o valor de interfaz, y cualquier matriz, sector, mapa o cadena de longitud cero
pero sin considerar una estructura con todos los valores vacíos / predeterminados. Todos sus campos también están etiquetados con omitempty
, pero esto no tiene ningún efecto.
¿Cómo puedo obtener el paquete JSON para no alinear mi campo que es una estructura vacía?
Como se mencionó en @chakrit en un comentario, no se puede lograr que esto funcione al implementar json.Marshaler
en MyStruct
, y la implementación de una función de clasificación JSON personalizada en cada estructura que lo use puede ser mucho más trabajo. Realmente depende de su caso de uso en cuanto a si vale la pena el trabajo adicional o si está preparado para vivir con estructuras vacías en su JSON, pero aquí está el patrón que uso aplicado a Result
:
type Result struct {
Data MyStruct
Status string
Reason string
}
func (r Result) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Data *MyStruct `json:"data,omitempty"`
Status string `json:"status,omitempty"`
Reason string `json:"reason,omitempty"`
}{
Data: &r.Data,
Status: r.Status,
Reason: r.Reason,
})
}
func (r *Result) UnmarshalJSON(b []byte) error {
decoded := new(struct {
Data *MyStruct `json:"data,omitempty"`
Status string `json:"status,omitempty"`
Reason string `json:"reason,omitempty"`
})
err := json.Unmarshal(b, decoded)
if err == nil {
r.Data = decoded.Data
r.Status = decoded.Status
r.Reason = decoded.Reason
}
return err
}
Si tienes estructuras enormes con muchos campos, esto puede volverse tedioso, especialmente si cambias la implementación de una estructura más adelante, pero si no reescribes todo el paquete json
para satisfacer tus necesidades (no es una buena idea), esta es la única forma en que puedo pensar hacer esto mientras se mantiene un MyStruct
no puntero allí.
Además, no tiene que usar estructuras en línea, puede crear nombres. Sin embargo, uso LiteIDE con código completo, así que prefiero inline para evitar el desorden.
Oh! Solución fácil: "cualquier puntero nulo". - hacer que la estructura sea un puntero.
Fijar:
type Result struct {
Data *MyStruct `json:"data,omitempty"`
Status string `json:"status,omitempty"`
Reason string `json:"reason,omitempty"`
}
Observe el *MyStruct
: cuando creo un MyStruct
ahora, simplemente lo hago por referencia:
myStruct := &MyStruct{ /* values */ }
Y ahora el MyStruct
"vacío" ya no se clasifica en JSON como se desee.
Data
son una estructura inicializada, por lo que no se consideran vacíos porque la encoding/json
solo mira el valor inmediato, no los campos dentro de la estructura.
Lamentablemente, devolver nil
desde json.Marhsler
actualmente no funciona:
func (_ MyStruct) MarshalJSON() ([]byte, error) {
if empty {
return nil, nil // unexpected end of JSON input
}
// ...
}
También podría darle a Result
un Marshaller, pero no vale la pena el esfuerzo.
La única opción, como sugiere Matt, es convertir Data
un puntero y establecer el valor en nil
.