英文:
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))
}
但是我真的不想使用单独的TermHash
和Term
类型,因为在代码中似乎是不必要的,只是为了将这些行添加到过滤器中。我能否避免使用它们?
我只想使用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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论