2016-07-20 17 views
4

Angesichts der folgenden Strukturen einzubetten:Idiomatic Weg Struktur mit benutzerdefinierten MarshalJSON() -Methode

type Person { 
    Name string `json:"name"` 
} 

type Employee { 
    Person 
    JobRole string `json:"jobRole"` 
} 

ich leicht einen Mitarbeiter zu JSON Marschall als erwartet:

p := Person{"Bob"} 
e := Employee{&p, "Sales"} 
output, _ := json.Marshal(e) 
fmt.Printf("%s\n", string(output)) 

Ausgang:

{"name": "Bob", "jobRole": "Verkäufe"}

Aber wenn die eingebettete Struktur eine benutzerdefinierte MarshalJSON() Methode hat ...

func (p *Person) MarshalJSON() ([]byte,error) { 
    return json.Marshal(struct{ 
     Name string `json:"name"` 
    }{ 
     Name: strings.ToUpper(p.Name), 
    }) 
} 

es bricht ganz:

p := Person{"Bob"} 
e := Employee{&p, "Sales"} 
output, _ := json.Marshal(e) 
fmt.Printf("%s\n", string(output)) 

Ergebnisse Jetzt in:

{ "name": "BOB "}

(Beachten Sie den auffälligen Mangel an jobRole Feld)

Dies ist leicht zu antizipieren ... die eingebettete Person Struktur implementiert die MarshalJSON() Funktion, die aufgerufen wird.

Das Problem ist, es ist nicht, was ich wollen. Was würde ich sein möchte:

{ "name": "BOB", "jobrole": "Sales"}

Das heißt, kodieren Employee 's Felder normalerweise, und verschieben zu Person' s MarshalJSON() Methode, um seine Felder zu marshale, und etwas sauberes JSON zurückgeben.

nun auch eine MarshalJSON() Methode Employee hinzufügen, konnte ich aber dies erfordert, dass ich weiß, dass der eingebettete Typ MarshalJSON() auch implementiert, und entweder (a) duplizieren seine Logik, oder (b) nenne Person ‚s MarshalJSON() und manipuliere irgendwie seine Ausgabe, um zu passen, wo ich es will. Jeder Ansatz erscheint schlampig und nicht sehr zukunftssicher (was ist, wenn ein eingebetteter Typ, den ich eines Tages nicht kontrolliere, eine benutzerdefinierte MarshalJSON() Methode hinzufügt?)

Gibt es hier irgendwelche Alternativen, die ich nicht berücksichtigt habe?

+0

Was passiert, wenn Person des 'MarshalJSON' ein Array zurückgegeben? Es gibt keine Möglichkeit, das zu einem Objekt zusammenzuführen. Die Zusammensetzung ist schwer. –

+0

@AlexGuerra: Ganz. Es ist genug, um mir zu wünschen, dass MarshalJSON aus Konsistenzgründen eingebettete Typen immer überspringt. heh. Ich nehme an, dass in meiner Bewerbung ein völlig anderer Ansatz erforderlich ist. – Flimzy

Antwort

4

Setzen Sie MarshalJSON nicht auf Person, da das zum äußeren Typ befördert wird. Stattdessen machen Sie eine type Name string und haben Name implementieren MarshalJSON.Dann Person ändern

type Person struct { 
    Name Name `json:"name"` 
} 

Beispiel: https://play.golang.org/p/u96T4C6PaY


aktualisieren

Um dies zu lösen allgemeiner wirst du MarshalJSON auf der äußeren Typ zu implementieren. Methoden auf dem inneren Typ werden zum äußeren Typ befördert, so dass Sie das nicht umgehen werden. Sie könnten den äußeren Typ den MarshalJSON des inneren Typs nennen lassen und ihn dann in eine generische Struktur wie map[string]interface{} unmarshalen und Ihre eigenen Felder hinzufügen. Dieses Beispiel macht, dass aber es hat eine Nebenwirkung, die Reihenfolge der endgültigen Ausgabefelder ändern

https://play.golang.org/p/ut3e21oRdj

+0

Das funktioniert für mein spezifisches Beispiel und ist hilfreich (so +1), aber ich denke, es fehlt der Hauptpunkt dessen, was ich zu fragen versuchte. Was passiert, wenn MarshalJSON von Person neue Felder oder andere undurchsichtige Daten hinzufügt? – Flimzy

+0

@Flimzy guter Punkt. Ich habe meine Antwort mit einem anderen Beispiel aktualisiert – jcbwlkr