我想使用自定义标签编组/解组 Golang 对象 (json)。
喜欢
type Foo struct {
Bar string `json:"test" es:"bar"`
}
data, _ := json.MarshalWithESTag(Foo{"Bar"})
log.Println(string(data)) // -> {"foo":"bar"}
Run Code Online (Sandbox Code Playgroud)
换句话说,我想在这里使用带有不同标签的encoding/json库: https: //github.com/golang/go/blob/master/src/encoding/json/encode.go#L1033
谢谢 :)
我认为您编写示例的方式可能有点不正确?
\n当我使用Marshal()inplace of运行您的代码时MarshalWithESTag(),我得到的结果{"test":"Bar"}并不{"foo":"test"}像您的示例所暗示的那样。\n这是在 Go Playground 中运行的代码来说明输出:
package main\n\nimport (\n "encoding/json"\n "fmt"\n)\ntype Foo struct {\n Bar string `json:"test" es:"bar"`\n}\nfunc main() {\n data, _ := json.Marshal(Foo{"Bar"})\n fmt.Println(string(data))\n}\nRun Code Online (Sandbox Code Playgroud)\n假设我对您想要的内容是正确的,那么这意味着您真正想要的是{"bar":"Bar"}您调用时的输出json.MarshalWithESTag()。
基于这个假设,您可以使用以下代码 \xe2\x80\x94 来完成,您可以在 Go Playground \xe2\x80\x94 中看到它,之后我将解释该代码。(如果我的假设不正确,我也会解决这个问题):
\n您无法将MarshalWithESTag()方法添加到包中json,因为 Go 不允许安全的 猴子修补。但是,您可以向结构添加一个MarshalWithESTag()方法Foo,此示例还向您展示了如何调用它:
func (f Foo) MarshalWithESTag() ([]byte, error) {\n data, err := json.Marshal(f)\n return data,err\n}\n\nfunc main() {\n f := &Foo{"Bar"}\n data, _ := f.MarshalWithESTag()\n log.Println(string(data)) // -> {"bar":"Bar"}\n}\nRun Code Online (Sandbox Code Playgroud)\n接下来,您需要向结构添加一个MarshalJSON()方法Foo。json.Marshal()当您调用并向其传递一个实例时,它将被调用Foo。
以下是一个简单的示例,它对 的返回值进行了硬编码{"hello":"goodbye"},以便您可以在 Playground 中看到添加 aMarshalJSON()会如何Foo影响json.Marshal(Foo{"Bar"}):
func (f Foo) MarshalJSON() ([]byte, error) {\n return []byte(`{"hello":"goodbye"}`),nil\n}\nRun Code Online (Sandbox Code Playgroud)\n其输出将是:
\n{"hello":"goodbye"}\nRun Code Online (Sandbox Code Playgroud)\n在方法内部MarshalJSON(),我们需要使用es 标签而不是json标签生成 JSON,这意味着我们需要在方法内生成 JSON,因为 Go 不向我们提供 JSON;它期望我们生成它。
在 Go 中生成 JSON 最简单的方法是使用json.Marshal(). 然而,如果我们使用json.Marshal(f)where是一个在调用时作为接收者传递的f实例,它将最终陷入无限递归循环!解决方案是基于 的现有类型创建一个新的结构类型,除了其标识外,该类型与现有类型相同。基于以下内容创建新类型非常简单:FooMarshalJson()FooesFooFoo
type esFoo Foo\nRun Code Online (Sandbox Code Playgroud)\n既然我们已经有了,esFoo我们现在可以将我们的实例转换Foo为类型esFoo以打破与我们的自定义的关联MarshalJSON()。这是有效的,因为我们的方法特定于具有标识的类型Foo,而不是类型esFoo。esFoo传递to的实例json.Marshal()允许我们使用从 Go 获得的默认 JSON 编组。\n
为了说明这一点,您可以在此处看到一个示例,该示例使用esFoo并设置其Bar属性来为"baz"我们提供输出{"test":"baz"} (您还可以看到它在 Go 中运行)操场):
type esFoo Foo\nfunc (f Foo) MarshalJSON() ([]byte, error) {\n es := esFoo(f)\n es.Bar = "baz"\n _json,err := json.Marshal(es)\n return _json,err\n}\nRun Code Online (Sandbox Code Playgroud)\n其输出将是:
\n{"test":"baz"}\nRun Code Online (Sandbox Code Playgroud)\n接下来我们处理和操作里面的JSON MarshalJSON()。这可以通过使用变量来完成,然后我们可以使用类型断言将该变量视为映射。这是一个与前面的示例无关的独立示例,通过打印说明了这一点(您再次可以看到它在 Go Playground 中工作):json.Unmarshal()interface{}map[maker:Chevrolet model:Corvette year:2021]
package main\n\nimport (\n "encoding/json"\n "fmt"\n)\ntype Car struct {\n Maker string `json:"maker" es:"fabricante"`\n Model string `json:"model" es:"modelo"`\n Year int `json:"year" es:"a\xc3\xb1o"` \n}\nvar car = Car{\n Maker:"Chevrolet",\n Model:"Corvette",\n Year:2021,\n}\n\nfunc main() {\n _json,_ := json.Marshal(car)\n var intf interface{}\n _ = json.Unmarshal(_json, &intf)\n m := intf.(map[string]interface{}) \n fmt.Printf("%v",m)\n}\nRun Code Online (Sandbox Code Playgroud)\n其输出将是:
\nmap[maker:Chevrolet model:Corvette year:2021]\nRun Code Online (Sandbox Code Playgroud)\n我们的下一个挑战是访问标签。可以使用Reflection访问标签。Go 在标准反射包中提供了反射功能。\n
使用Car上面的结构,这是一个简单的示例,说明了如何使用反射。它使用该reflect.TypeOf()函数来检索类型作为值,然后内省该类型以检索每个字段的标签。检索每个标签的代码是 t.Field(i).Tag.Lookup("es"),希望它是不言自明的(再次,请在 Go Playground 中查看):
func main() {\n t := reflect.TypeOf(car) \n for i:=0; i<t.NumField();i++{\n tag, _ := t.Field(i).Tag.Lookup("es")\n fmt.Printf("%s\\n",tag)\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n其输出将是:
\nfabricante\nmodelo\na\xc3\xb1o\nRun Code Online (Sandbox Code Playgroud)\n现在我们已经涵盖了所有构建块,我们可以将它们整合到一个可行的解决方案中。唯一值得一提的补充是创建一个_m长度相同的新映射变量,以便m我们可以使用标签存储值es:
func (f Foo) MarshalJSON() ([]byte, error) {\n es := esFoo(f)\n _json,err := json.Marshal(es)\n {\n if err != nil {\n goto end\n }\n var intf interface{}\n err = json.Unmarshal(_json, &intf)\n if err != nil {\n goto end\n }\n m := intf.(map[string]interface{})\n _m := make(map[string]interface{},len(m))\n t := reflect.TypeOf(f)\n i := 0\n for _,v := range m {\n tag, found := t.Field(i).Tag.Lookup("es")\n if !found {\n continue\n }\n _m[tag] = v\n i++\n }\n _json,err = json.Marshal(_m)\n }\nend:\n return _json,err\n}\nRun Code Online (Sandbox Code Playgroud)\n然而,还有一个细节尚未完成。上述所有代码f.MarshalWithESTag()将为es标签生成 JSON,但json.Marshal(f)我们也希望后者返回其对json标签的使用。\n
因此,我们只需要:
A。useESTags添加一个初始值为 的本地包变量false,
b. 修改f.MarshalWithESTag()为设置useESTags为true调用前json.Marshal(),然后
C。useESTags回到返回前的状态false,并且
d. 最后修改MarshalJSON()为仅执行标签所需的逻辑,es如果useESTags设置为true:\n
这将我们带到最终代码 \xe2\x80\x94 ,其中第二个属性Foo提供了更好的示例(最后,您当然可以看到在Go Playground 中):
package main\n\nimport (\n "encoding/json"\n "log"\n "reflect"\n)\n\ntype Foo struct {\n Foo string `json:"test" es:"bar"`\n Bar string `json:"live" es:"baz"`\n}\ntype esFoo Foo\nvar useESTags = false\nfunc (f Foo) MarshalWithESTag() ([]byte, error) {\n useESTags = true\n data, err := json.Marshal(f)\n useESTags = false\n return data,err\n}\nfunc (f Foo) MarshalJSON() ([]byte, error) {\n es := esFoo(f)\n _json,err := json.Marshal(es)\n if useESTags {\n if err != nil {\n goto end\n }\n var intf interface{}\n err = json.Unmarshal(_json, &intf)\n if err != nil {\n goto end\n }\n m := intf.(map[string]interface{})\n _m := make(map[string]interface{},len(m))\n t := reflect.TypeOf(f)\n i := 0\n for _,v := range m {\n tag, found := t.Field(i).Tag.Lookup("es")\n if !found {\n continue\n }\n _m[tag] = v\n i++\n }\n _json,err = json.Marshal(_m)\n }\nend:\n return _json,err\n}\n\nfunc main() {\n f := &Foo{"Hello","World"}\n data, _ := json.Marshal(f)\n log.Println(string(data)) // -> {"test":"Hello","live":"World"}\n data, _ = f.MarshalWithESTag()\n log.Println(string(data)) // -> {"bar":"Hello","baz":"World"}\n}\nRun Code Online (Sandbox Code Playgroud)\n如果我的假设是错误的,我想我至少可以假设我提供的这段代码足以让您实现您的目标。如果根据所显示的技术确实是您想要的,那么您应该能够交换输出中的键和值。如果没有,请评论寻求帮助。
\n最后,如果不提及反射可能很慢,并且此示例对每个对象使用多次反射来实现所需的输出,那就太失职了。对于许多用例来说,以这种方式处理 JSON 所需的时间并不重要。然而,对于许多其他用例来说,执行时间可能会成为交易杀手。一些人评论说你应该以不同的方式处理这个问题;如果性能很重要和/或使用更惯用的 Go方法很重要,您可能需要认真考虑他们的建议。
\n| 归档时间: |
|
| 查看次数: |
9195 次 |
| 最近记录: |