英文:
Add a field to JSON ( struct + interface ) golang
问题
这是响应接口:
type Response interface{}
它由以下结构体满足:
type CheckResponse struct {
Status string `json:"status"`
}
我得到的输出是out []Response
,需要在发送之前向该JSON添加一个Version
字符串。我尝试使用匿名结构体(但失败了):
for _, d := range out {
outd := struct {
Resp Response `json:",inline"`
Version string `json:",inline"`
}{
Resp: d,
Version: "1.1",
}
data, _ := json.Marshal(outd)
log.Infof("response: %s", data)
}
我得到的输出是:
response: {"Resp":{"status":"UP"},"Version":"1.1"}
我想要的是
{"status":"UP","Version":"1.1"}
即一个单一的扁平JSON。
英文:
Here's the response interface :
type Response interface{}
It's satisfied by a struct like this :
type CheckResponse struct {
Status string `json:"status"`
}
I am getting out []Response
as an output which is to be consumed elsewhere.
I want to add a Version
string to this JSON, before it's being sent. I've tried using anonymous structs ( but in vain ) :
for _, d := range out {
outd := struct {
Resp Response `json:",inline"`
Version string `json:",inline"`
}{
Resp: d,
Version: "1.1",
}
data, _ := json.Marshal(outd)
log.Infof("response : %s", data)
}
The output I am getting is :
response : {"Resp":{"status":"UP"},"Version":"1.1"}
What I want is
{"status":"UP","Version":"1.1"}
i.e. one single flat JSON.
答案1
得分: 4
断言你的 d
为 CheckResponse
类型,然后定义动态结构如下所示:
outd := struct {
Resp string `json:"status,inline"`
Version string `json:",inline"`
}
这是完整的代码:
package main
import (
"encoding/json"
"fmt"
)
type Response interface{}
type CheckResponse struct {
Status string `json:"status"`
}
func main() {
out := []Response{
CheckResponse{Status: "UP"},
}
for _, d := range out {
res, ok := d.(CheckResponse)
if !ok {
continue
}
outd := struct {
Resp string `json:"status,inline"`
Version string `json:",inline"`
}{
Resp: res.Status,
Version: "1.1",
}
data, _ := json.Marshal(outd)
fmt.Printf("response : %s", data)
}
}
你可以在这里运行。
英文:
Assert your d
to CheckResponse type and then define dynamic struct like this
outd := struct {
Resp string `json:"status,inline"`
Version string `json:",inline"`
}
This is the full code for this.
package main
import (
"encoding/json"
"fmt"
)
type Response interface {}
type CheckResponse struct {
Status string `json:"status"`
}
func main() {
out := []Response{
CheckResponse{Status: "UP"},
}
for _, d := range out {
res, ok := d.(CheckResponse)
if !ok {
continue
}
outd := struct {
Resp string `json:"status,inline"`
Version string `json:",inline"`
}{
Resp: res.Status,
Version: "1.1",
}
data, _ := json.Marshal(outd)
fmt.Printf("response : %s", data)
}
}
You can run here
答案2
得分: 2
inline
标签不受encoding/json
支持,并且嵌入接口也无法产生您想要的结果。您需要为out
值声明一个类型,并使该类型实现json.Marshaler
接口,然后可以自定义其字段的编组方式。例如,您可以分别编组Resp和Version两个字段,然后将结果"合并"为一个单独的JSON对象。
type VersionedResponse struct {
Resp Response
Version string
}
func (r VersionedResponse) MarshalJSON() ([]byte, error) {
out1, err := json.Marshal(r.Resp)
if err != nil {
return nil, err
}
out2, err := json.Marshal(struct{ Version string }{r.Version})
if err != nil {
return nil, err
}
// 注意:如果Resp可以持有某些在编组后产生非JSON对象的内容,
// 您需要更加小心地合并这两个输出。
//
// 例如,如果Resp为nil,则out1将是[]byte(`null`)
// 如果Resp是一个切片,则out1将是[]byte(`[ ... ]`)
out1[len(out1)-1] = ',' // 将 '}' 替换为 ','
out2 = out2[1:] // 移除开头的 '{'
return append(out1, out2...), nil
}
https://play.golang.org/p/66jIYXGUtWJ
英文:
inline
tag is not supported by encoding/json
and embedding interfaces will also not produce the result you want. You'll have to declare a type for the out
value and have that type implement the json.Marshaler
interface, you can then customize how its fields are marshaled, for example you could marshal the two fields Resp and Version separately and then "merge the result" into a single json object.
type VersionedResponse struct {
Resp Response
Version string
}
func (r VersionedResponse) MarshalJSON() ([]byte, error) {
out1, err := json.Marshal(r.Resp)
if err != nil {
return nil, err
}
out2, err := json.Marshal(struct{ Version string }{r.Version})
if err != nil {
return nil, err
}
// NOTE: if Resp can hold something that after marshaling
// produces something other than a json object, you'll have
// to be more careful about how you gonna merge the two outputs.
//
// For example if Resp is nil then out1 will be []byte(`null`)
// If Resp is a slice then out1 will be []byte(`[ ... ]`)
out1[len(out1)-1] = ',' // replace '}' with ','
out2 = out2[1:] // remove leading '{'
return append(out1, out2...), nil
}
答案3
得分: 1
一种肯定有效的方法是简单地使用map[string]interface{}
,通过反射迭代Response
中的字段,或者使用像structs
这样的库,使用响应字段更新您的映射,将您的版本字段附加到映射中,然后进行编组。
这里有一个示例
package main
import (
"encoding/json"
"fmt"
"github.com/fatih/structs"
)
type Response interface{}
type CheckResponse struct {
Status string `json:"status"`
}
func main() {
resp := CheckResponse{Status: "success"}
m := structs.Map(resp)
m["Version"] = "0.1"
out, _ := json.Marshal(m)
fmt.Println(string(out))
}
英文:
One way that will work for sure is simply use a map[string]interface{}
, iterate over fields in Response
via reflect or use a library like structs
, update your map with response fields, append your version field to map, and then marshal.
Here is an example
package main
import (
"encoding/json"
"fmt"
"github.com/fatih/structs"
)
type Response interface{}
type CheckResponse struct {
Status string `json:"status"`
}
func main() {
resp := CheckResponse{Status: "success"}
m := structs.Map(resp)
m["Version"] = "0.1"
out, _ := json.Marshal(m)
fmt.Println(string(out))
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论