Golang изменить json без структуры

type Struct struct {
   Value  string `json:"value"`
   Value1 string `json:"value_one"`
   Nest   Nested `json:"nest"`
}

type Nested struct {
   Something string `json:"something"`
}

Я хочу добавить элементы, которые не входят в определения структур, не создавая другого типа структуры. Например

Struct.Extra1 = Nested{"yy"}
Struct.Nested.Extra2 = "zz"

Что приведет к

{
    "Value": "xx",
    "Value1": "xx",
    "Extra1": {
      "Something", "yy"
    },
    "Nest": {
      "Something": "xx",
      "Extra2": "zz"
    }
}

SOLUTION1: Я думал о добавлении omitempty для достижения этого, но он делает сложные структуры.

type Struct struct {
   Value  string
   Value1 string
   Nest   Nested
   Extra1 Nested `json:"omitempty"`
}

type Nested struct {
   Something string
   Extra2 string `json:"omitempty"`
}

SOLUTION2:

myextras := make(map[string]interface{})
// get Struct.Nested in map[string]interface{} format
myextras = Struct.Nest
myextras["Extra2"] = "zz"

// get Struct in map[string]interface{} format
struct["Nest"] = myextras
struct["Extra1"] = Nested{"yy"}

// solves the problem with lots of type casting but doesn't support json tag naming

Есть ли лучшее решение для добавления вложенных элементов, которые не представлены в struct datatype с поддержкой json-tag и могут использоваться для вывода пользователю.

Ответ 1

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

Вы можете сделать что-то вроде (demo: http://play.golang.org/p/dDiTwxhoNn):

package main

import (
    "encoding/json"
    "fmt"
    "log"
)

type Book struct {
    Title  string
    Author string

    // extra is used for additional dynamic element marshalling
    extra func() interface{}
}

type FakeBook Book

func (b *Book) SetExtra(fn func() interface{}) {
    b.extra = fn
}

func (b *Book) MarshalJSON() ([]byte, error) {
    if b.extra == nil {
        b.extra = func() interface{} { return *b }
    }

    return json.Marshal(b.extra())
}

func main() {
    ms := &Book{
        Title:  "Catch-22",
        Author: "Joseph Heller",
    }

    ms.SetExtra(func() interface{} {
        return struct {
            FakeBook
            Extra1 struct {
                Something string `json:"something"`
            } `json:"extra1"`
        }{
            FakeBook: FakeBook(*ms),
            Extra1: struct {
                Something string `json:"something"`
            }{
                Something: "yy",
            },
        }
    })

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

    mb := &Book{
        Title:  "Vim-go",
        Author: "Fatih Arslan",
    }

    mb.SetExtra(func() interface{} {
        return struct {
            FakeBook
            Something string `json:"something"`
        }{
            FakeBook:  FakeBook(*mb),
            Something: "xx",
        }
    })

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

    mc := &Book{
        Title:  "Another-Title",
        Author: "Fatih Arslan",
    }

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

Ответ 2

да. существует тип json.Raw, который не является структурным, но [] байтом. вы можете управлять им из структуры, любым маршалом/немаршальным способом.

Ответ 3

Подход к карте - единственный разумный способ сделать это, все остальное (например, поля json.RawMessage в любом случае потребуют дополнительного шага маршаллинга.

Ответ 4

Если кто-то не в восторге от предоставленного решения:

Попробуйте tidwall/sjson. Он предоставляет функции для быстрого редактирования JSON без определения какой-либо структуры. Это сэкономило мне кучу времени вчера: D

Пример использования:

value, _ := sjson.Set('{"name":{"last":"Anderson"}}', "name.last", "Smith")
println(value)

// Output:
// {"name":{"last":"Smith"}}