[] struct中不需要的附加类型

I'm learning go and stuck structs

I need to generate json:

{
    "and" : [
        { "term" : { "name.second" : "ba" } }
      ]
    }
  }

So i can do it with code:

package main

import (
  "encoding/json"
  "fmt"
)

type Term map[string]interface{}

type TermHash struct {
  Term `json:"term"`
}

type Filter struct {
        And []TermHash `json:"and"`
}

func main() {
  var filter Filter
  filter.And = append(filter.And, TermHash{ Term{"name.second" : "ba"}})
  jsonFilter, _ := json.MarshalIndent(filter, "", "   ")
  fmt.Printf(string(jsonFilter))
}

But I really do not want use separate TermHash and Term types, its seems unnecessary in code and used only to add this lines to filter. Can i avoid of using it?

I just want to accomplish this with only type Filter:

type Filter struct {
  And []struct{
    Term map[string]interface{} `json:"term"`
  } `json:"and"`
}

This looks more readable and represent expected result, but I can't create instances of Term in this way. There is a way to add Terms line to json without creating separate types?

What you really want is a custom way to JSON-encode Term. Rather than a simple map, it marshals as if it were an object containing a map. So, let's write that custom MarshalJSON for it:

type Term map[string]interface{}

func (t Term) MarshalJSON() ([]byte, error) {
    return json.Marshal(struct {
        T map[string]interface{} `json:"term"`
    }{t})
}

We create an anonymous struct here, fill it with ourselves, and then marshal it to JSON. Note the use of map[string]interface{} here. While that looks like Term, it's actually a different type with its own way of being JSON encoded. If you tried to save some typing here and use T Term, you'd find yourself in an infinite loop. (This idea of creating new types that have the same structure as other types is a major part of Go.)

Now our data structure is simple; just a slice of Term:

type Filter struct {
    And []Term `json:"and"`
}

func main() {
    var filter Filter
    filter.And = append(filter.And, Term{"name.second" : "ba"})
    jsonFilter, _ := json.MarshalIndent(filter, "", "   ")
    fmt.Printf(string(jsonFilter))
}

That said, you could also go the other way and make your data model more closely match the JSON. In that case Term should be a struct, not a map. You'd probably write it this way:

type Term struct {
    Values map[string]interface{} `json:"term"`
}

func NewTerm(key, value string) Term {
    return Term{map[string]interface{}{key: value}}
}

type Filter struct {
    And []Term `json:"and"`
}

func main() {
    var filter Filter
    filter.And = append(filter.And, NewTerm("name.second", "ba"))
    jsonFilter, _ := json.MarshalIndent(filter, "", "   ")
    fmt.Printf(string(jsonFilter))
}

You can create a wrapper function around that.

package main

import (
    "fmt"
    "encoding/json"
)

type Filter struct {    
    And    []map[string]interface{}  `json:"and"`
}

func WrapFilter(i interface{}) Filter {
    return Filter{
        And: []map[string]interface{}{
            map[string]interface{}{
                "term": i,
            },
        },
    }
}

func main() {

    f := WrapFilter(map[string]string{"name.second": "ba"}) 
    json.Marshal(f) 

}

This way, you will always have "term" has the second-level key in your marshaled JSON.