2016-07-20 2 views
4

Учитывая следующие Структуры:Идиоматический способ встроить-структуру с пользовательским MarshalJSON() методом

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

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

Я могу легко МАРШАЛ служащего JSON, как и ожидался:

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

Выход:

{"name": "Bob", "jobRole": "Sales"}

Но когда внедренный структура имеет метод пользовательских MarshalJSON() ...

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

он распадается полностью:

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

В настоящее время результаты в:

{ "имя": "ЛПП "}

(Обратите внимание на заметное отсутствие jobRole field)

Это легко предвидится ... встроенная функция Person struct реализует функцию, которая вызывается, MarshalJSON().

Проблема в том, что это не то, что я хочу. То, что я хочу бы:

{ "имя": "БОБ", "jobRole": "Продажи"}

То есть, закодировать Employee 'поля s нормально, и отложить до Person' s MarshalJSON() метод для маршалирования его полей и отдать некоторые аккуратные JSON.

Теперь я мог бы добавить метод MarshalJSON() к Employee, а также, но это требует, чтобы я знаю, что встроенный тип реализует MarshalJSON(), а также, и либо (а) дублировать логику, или (б) называют Person «s MarshalJSON() и как-то манипулировать его выходом, чтобы он соответствовал мне. Любой подход кажется неаккуратным и не очень перспективным доказательством (что, если встроенный тип, который я не могу контролировать, добавляет пользовательский метод MarshalJSON()?)

Есть ли альтернативы здесь, которые я не рассматривал?

+0

Что делать, если лицо в 'MarshalJSON' возвращается массив? Невозможно объединить это в объект. Композиция сложная. –

+0

@AlexGuerra: Совершенно. Этого достаточно, чтобы заставить меня пожелать, чтобы Маршал Джейсон всегда пропускал встроенные типы, ради обеспечения последовательности. хе. По-моему, в моем приложении, вероятно, требуется совсем другой подход. – Flimzy

ответ

4

Не ставьте MarshalJSON на Person, так как это повышается до внешнего типа. Вместо этого сделайте type Name string и поставьте NameMarshalJSON.Затем измените Person на

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

Пример: https://play.golang.org/p/u96T4C6PaY


Update

Чтобы решить эту проблему в более общем плане вы будете иметь, чтобы реализовать MarshalJSON на внешнем типе. Методы внутреннего типа продвигаются к внешнему типу, поэтому вы не сможете обойти это. Вы могли бы, чтобы внешний тип вызывал внутренний тип MarshalJSON, затем отменил его в общую структуру, такую ​​как map[string]interface{}, и добавьте свои собственные поля. Этот пример делает это, но она имеет побочный эффект изменения порядка конечных выходные полого

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

+0

Это работает для моего конкретного примера и полезно (так +1), но я думаю, что он пропускает основную часть того, что я пытался спросить. Что, если MarshalJSON Person добавит новые поля или другие непрозрачные данные? – Flimzy

+0

@Flimzy хорошая точка. Я обновил свой ответ другим примером – jcbwlkr