在Go语言中向JSON(结构体+接口)添加一个字段。

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

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

断言你的 dCheckResponse 类型,然后定义动态结构如下所示:

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
}

https://play.golang.org/p/66jIYXGUtWJ

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

huangapple
  • 本文由 发表于 2021年8月5日 14:40:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/68661761.html
匿名

发表评论

匿名网友

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

确定