Go: decoding json with one set of json tags, and encoding to a different set of json tags

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

Go: decoding json with one set of json tags, and encoding to a different set of json tags

问题

我有一个应用程序,从第三方 API 中获取数据。我需要将 JSON 解码为结构体,这要求结构体具有“传入”JSON 字段的 JSON 标签。传出的 JSON 字段具有不同的命名约定,因此我需要不同的 JSON 标签进行编码。

我将不得不对许多不同的结构体进行这样的操作,而且每个结构体可能有许多字段。

在不重复大量代码的情况下,如何最好地完成这个任务?

示例结构体:

// 传入的“schema”字段名称
type AccountIn struct {
OpenDate string json:"accountStartDate"
CloseDate string json:"cancelDate"
}

// 传出的“schema”字段名称
type AccountOut struct {
OpenDate string json:"openDate"
CloseDate string json:"closeDate"
}

英文:

I have an application that consumes data from a third-party api. I need to decode the json into a struct, which requires the struct to have json tags of the "incoming" json fields. The outgoing json fields have a different naming convention, so I need different json tags for the encoding.

I will have to do this with many different structs, and each struct might have many fields.

What is the best way to accomplish this without repeating a lot of code?

Example Structs:

// incoming "schema" field names
type AccountIn struct {
    OpenDate string `json:"accountStartDate"`
    CloseDate string `json:"cancelDate"`
}


// outgoing "schema" field names
type AccountOut struct {
    OpenDate string `json:"openDate"`
    CloseDate string `json:"closeDate"`
} 

答案1

得分: 3

一种不太常见但可能非常有效的方法是使用中间格式,这样您可以使用不同的读取器和写入器,从而使用不同的标签。例如,可以使用https://github.com/mitchellh/mapstructure将嵌套的映射结构转换为结构类型。这与json解组非常相似,只是从映射而不是json进行操作。

// 输入的“schema”字段名称
type AccountIn struct {
    OpenDate   string `mapstructure:"accountStartDate" json:"openDate"`
    CloseDate  string `mapstructure:"cancelDate" json:"closeDate"`
}

// 从json到映射,不更改字段名称
temporaryMap := map[string]interface{}{}
err := json.Unmarshal(jsonBlob, &temporaryMap)

// 使用mapstructure标签从映射到结构体
accountIn := &AccountIn{}
mapstructure.Decode(temporaryMap, accountIn)

稍后在编写(或读取)时,您将直接使用json函数,这些函数将使用json标签。

英文:

A bit an uncommon but probably quite well working method would be to use a intermediate format so u can use different readers and writers and therefore different tags. For example https://github.com/mitchellh/mapstructure which allows to convert a nested map structure into struct
types. Pretty similar like json unmarshal, just from a map.

// incoming "schema" field names
type AccountIn struct {
    OpenDate string `mapstructure:"accountStartDate" json:"openDate"`
    CloseDate string `mapstructure:"cancelDate" json:"closeDate"`
}

// from json to map with no name changes
temporaryMap := map[string]interface{}{}
err := json.Unmarshal(jsonBlob, &temporaryMap)

// from map to structs using mapstructure tags
accountIn := &AccountIn{}
mapstructure.Decode(temporaryMap, accountIn)

Later when writing (or reading) u will use directly the json functions which will then use the json tags.

答案2

得分: 3

也许Go 1.8即将到来的变化会对你有所帮助,它将允许即使JSON标签定义不同,也可以进行类型转换。这个示例https://play.golang.org/p/Xbsoa8SsEk在1.8beta上按预期工作,我想这会简化你目前的解决方案。

英文:

Maybe the coming change on Go 1.8 would help you, it will allow to 'cast' types even if its JSON tags definition is different: This https://play.golang.org/p/Xbsoa8SsEk works as expected on 1.8beta, I guess this would simplify your current solution

答案3

得分: 1

如果可以接受通过json.Unmarshaljson.Marshal进行另一次往返,并且在各种类型中没有任何模糊的字段名称,你可以通过将所有的JSON键在一次遍历中转换为json包使用的通用结构来实现:

// 将输入映射到输出的JSON标识符
var translation = map[string]string{
    "accountStartDate": "openDate",
    "cancelDate":       "closeDate",
}

func translateJS(js []byte) ([]byte, error) {
    var m map[string]interface{}
    if err := json.Unmarshal(js, &m); err != nil {
        return nil, err
    }

    translateKeys(m)
    return json.MarshalIndent(m, "", "  ")
}

func translateKeys(m map[string]interface{}) {
    for _, v := range m {
        if v, ok := v.(map[string]interface{}); ok {
            translateKeys(v)
        }
    }

    keys := make([]string, 0, len(m))
    for k := range m {
        keys = append(keys, k)
    }

    for _, k := range keys {
        if newKey, ok := translation[k]; ok {
            m[newKey] = m[k]
            delete(m, k)
        }
    }
}

链接:https://play.golang.org/p/nXmWlj7qH9

英文:

If it's acceptable to take another round trip through json.Unmarshal and json.Marshal, and you don't have any ambiguous field names within your various types, you could translate all the json keys in one pass by unmarshaling into the generic structures used by the json package:

// map incoming to outgoing json identifiers
var translation = map[string]string{
	"accountStartDate": "openDate",
	"cancelDate":       "closeDate",
}
   
func translateJS(js []byte) ([]byte, error) {
	var m map[string]interface{}
	if err := json.Unmarshal(js, &m); err != nil {
		return nil, err
	}

	translateKeys(m)
	return json.MarshalIndent(m, "", "  ")
}

func translateKeys(m map[string]interface{}) {
	for _, v := range m {
		if v, ok := v.(map[string]interface{}); ok {
			translateKeys(v)
		}
	}

	keys := make([]string, 0, len(m))
	for k := range m {
		keys = append(keys, k)
	}

	for _, k := range keys {
		if newKey, ok := translation[k]; ok {
			m[newKey] = m[k]
			delete(m, k)
		}
	}
}

https://play.golang.org/p/nXmWlj7qH9

答案4

得分: 0

这可能是一种天真的方法,但实现起来相当简单:-

func ConvertAccountInToAccountOut(AccountIn incoming) (AccountOut outcoming){
    var outcoming AccountOut
    outcoming.OpenDate = incoming.OpenDate
    outcoming.CloseDate = incoming.CloseDate

    return outcoming
}

var IncomingJSONData AccountIn
resp := getJSONDataFromSource()  // 从某个方法获取输入的 JSON 数据
err1 := json.UnMarshall(resp,&IncomingJSONData)
OutGoingJSONData := ConvertAccountInToAccountOut(IncomingJSONData)

if err1 != nil {
    fmt.Println("在解析 JSON 时出错",err1)
}

fmt.Println("输出的 JSON 数据:",OutGoingJSONData)
英文:

This might be a Naive Approach but is fairly easy to implement:-

func ConvertAccountInToAccountOut(AccountIn incoming) (AccountOut outcoming){
    var outcoming AccountOut
    outcoming.OpenDate = incoming.OpenDate
    outcoming.CloseDate = incoming.CloseDate

    return outcoming
}

var IncomingJSONData AccountIn
resp := getJSONDataFromSource()  // Some method that gives you the Input JSON
err1 := json.UnMarshall(resp,&IncomingJSONData)
OutGoingJSONData := ConvertAccountInToAccountOut(IncomingJSONData)

if err1 != nil {
    fmt.Println("Error in UnMarshalling JSON ",err1)
}

fmt.Println("Outgoing JSON Data: ",OutGoingJSONData)

huangapple
  • 本文由 发表于 2017年1月10日 05:04:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/41556903.html
匿名

发表评论

匿名网友

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

确定