在[]struct中不需要的append类型

huangapple go评论95阅读模式
英文:

Unwanted types for append in []struct

问题

我正在学习Go语言,遇到了结构体的问题。

我需要生成JSON:

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

我可以用以下代码实现:

package main

import (
    "encoding/json"
    "fmt"
)

type Term map[string]interface{}

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

func main() {
    var filter Filter
    filter.And = append(filter.And, struct{
        Term map[string]interface{} `json:"term"`
    }{
        Term{"name.second": "ba"},
    })
    jsonFilter, _ := json.MarshalIndent(filter, "", "   ")
    fmt.Printf(string(jsonFilter))
}

但是我真的不想使用单独的TermHashTerm类型,因为在代码中似乎是不必要的,只是为了将这些行添加到过滤器中。我能否避免使用它们?

我只想使用Filter类型来实现这个目标:

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

这看起来更易读,也能表示预期的结果,但是我无法以这种方式创建Term的实例。有没有一种方法可以在不创建单独的类型的情况下将Term行添加到JSON中?

英文:

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?

答案1

得分: 3

你真正想要的是一种自定义的方式来将Term编码为JSON。它不是简单地映射为一个map,而是按照一个包含map的对象进行编组。因此,让我们为它编写自定义的MarshalJSON方法:

type Term map[string]interface{}

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

我们在这里创建了一个匿名的struct,用自身填充它,然后将其编组为JSON。请注意这里使用的map[string]interface{}。虽然它看起来像是Term,但实际上它是一个具有自己的JSON编码方式的不同类型。如果你在这里尝试节省一些输入并使用T Term,你会发现自己陷入无限循环中。(创建具有与其他类型相同结构的新类型的想法是Go的一个重要部分。)

现在我们的数据结构很简单,只是一个Term的切片:

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

main函数中:

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

另外,你也可以选择另一种方式,使你的数据模型更接近JSON的结构。在这种情况下,Term应该是一个结构体,而不是一个map。你可能会这样编写它:

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))
}
英文:

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))
}

答案2

得分: 1

你可以在这个函数周围创建一个包装函数。

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)	

}

这样,你在序列化为JSON时,"term" 将始终作为第二级键存在。

英文:

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.

huangapple
  • 本文由 发表于 2015年11月19日 21:33:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/33805396.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定