Могу ли я использовать MarshalJSON для добавления произвольных полей в json-кодирование в golang?

Предположим, что я написал следующий фрагмент кода. Полный код на игровой площадке здесь для тех, кто склонен.

type Book struct {
  Title        string
  Author       string
}

func main() {
  ms := Book{"Catch-22", "Joseph Heller"}
  out, err := json.MarshalIndent(ms, "", "  ")
  if err != nil {
    log.Fatalln(err)
  }
  fmt.Println(string(out))
}

Этот код выводит следующее, точно так же, как я ожидал:

{
  "Title": "Catch-22",
  "Author": "Joseph Heller"
}

Предположим, что на какой-то момент я хотел добавить поле в вывод JSON, не включая его в структуру Book. Возможно, жанр:

{
  "Title": "Catch-22",
  "Author": "Joseph Heller",
  "Genre": "Satire"
}

Можно ли использовать MarshalJSON() для добавления произвольного поля в полезную нагрузку JSON на Marshal()? Что-то вроде:

func (b *Book) MarshalJSON() ([]byte, error) {
    // some code
}

Другие ответы заставляют меня думать, что это должно быть возможно, но я изо всех сил пытаюсь понять реализацию.

Ответ 1

Вот лучший ответ, чем предыдущий.

type FakeBook Book

func (b Book) MarshalJSON() ([]byte, error) {
    return json.Marshal(struct {
        FakeBook
        Genre string
    }{
        FakeBook: FakeBook(b),
        Genre:    "Satire",
    })
}

Поскольку анонимные поля структуры "объединены" (с несколькими дополнительными соображениями), мы можем использовать это, чтобы избежать переназначения отдельных полей. Обратите внимание на использование типа FakeBook, чтобы избежать бесконечной рекурсии, которая в противном случае возникла бы.

Игровая площадка: http://play.golang.org/p/21YXhB6OyC

Ответ 3

Маршаллинг a map - это еще один способ решения проблемы.

tmap := make(map[string]interface{})

tmap["struct"] = struct {
    StructValue string `json:"struct_value"`
}{
    "Value 02",
}

tmap["string"] = "Value 01"

out, err := json.MarshalIndent(tmap, "", "  ")
if err != nil {
    log.Fatalln(err)
}
fmt.Println(string(out))

Это выведет:

{
  "string": "Value 01",
  "struct": {
    "struct_value": "Value 02"
  }
}

Если у вас много произвольных ключевых имен, это может быть хорошим решением.

Игровая площадка: https://play.golang.org/p/Umy9rtx2Ms