2017-10-16 27 views
2
package main 

import (
    "fmt" 
    "encoding/json" 
) 

type Ticket struct { 
    From string 
    To string 
} 

func (t Ticket) String() string { 
    return fmt.Sprintf("%s - %s", t.From, t.To) 
} 

type Passenger struct { 
    Name string `json:"Name"` 
    Tkt Ticket `json:"Ticket"` 
} 

func main() { 
    p := Passenger{} 
    p.Name = "John" 
    p.Tkt.From = "New York" 
    p.Tkt.To = "Washington" 

    buf, _ := json.Marshal(p) 
    fmt.Println(string(buf)) 
} 

Ce sorties de code:Comment sérialiser correctement la structure incorporée avec la méthode String() à la chaîne JSON?

{"Name":"John","Ticket":{"From":"New York","To":"Washington"}} 

Mais, en utilisant la méthode json.Marshal() (il est facile et convivial pour struct complexe), comment le faire sortie comme ceci:

{"Name":"John","Ticket":"New York - Washington"} 

Répondre

3

Pour générer le JSON Représentation d'une valeur Go, le package encoding/json vérifie si la valeur implémente les interfaces json.Marshaler ou encoding.TextMarshaler, et si c'est le cas, elles sont utilisées/appelées (dans cet ordre). Ceci est documenté à json.Marshal():

Marshal traverse la valeur v de manière récursive. Si une valeur rencontrée implémente l'interface Marshaler et n'est pas un pointeur nul, Marshal appelle sa méthode MarshalJSON pour produire JSON. Si aucune méthode MarshalJSON n'est présente mais que la valeur implémente encoding.TextMarshaler à la place, Marshal appelle sa méthode MarshalText et code le résultat en tant que chaîne JSON.

Le package json/encoding ne se soucie pas de la méthode String(). Donc, si vous voulez contrôler la représentation JSON/sortie de votre valeur (la struct Ticket), mettre en œuvre json.Marshaler sur elle (dans lequel vous pouvez appeler String() à votre goût):

func (t Ticket) MarshalJSON() ([]byte, error) { 
    return []byte(`"` + t.String() + `"`), nil 
} 

Ensuite, la sortie sera comme vous désir:

{"Name":"John","Ticket":"New York - Washington"} 

Essayez-le sur le Go Playground.

Une chose à regarder dehors pour: si le string produit par Ticket.String() contiendrait un guillemet ", la sortie deviendrait invalide JSON, ou plus probablement json.Marshal() retournerait une erreur.

Pour prendre soin de ces Escaping, le meilleur/plus simple est d'utiliser lui-même le paquet json: dire à JSON-encode le résultat string de Ticket.String():

func (t Ticket) MarshalJSON() ([]byte, error) { 
    return json.Marshal(t.String()) 
} 

Maintenant, si nous testons comme ça :

p.Name = "John" 
p.Tkt.From = "New\" York" // CONTAINS QUOTE 
p.Tkt.To = "Washington" 

buf, err := json.Marshal(p) 
fmt.Println(string(buf), err) 

la sortie sera toujours un JSON valide (essayer sur le Go Playground):

{"Name":"John","Ticket":"New\" York - Washington"} <nil> 
+0

Solution merveilleuse. Je vous remercie. –

+0

Incidemment, vous pouvez simplement utiliser '% q' au lieu de'% s' et il vous indiquera la chaîne. https://play.golang.org/p/RYhaMM5kpd – Kaedys

+1

@Kaedys Vous m'avez fait réaliser que j'avais oublié les citations potentielles dans l'entrée. J'ai mis à jour la question pour suggérer l'utilisation du paquet 'json' pour prendre soin de ceux-ci. – icza