忽略在编组(marshalling)时的JSON标签。

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

Ignore JSON tags when marshalling

问题

我正在从外部源获取JSON数据。这个JSON中的字段名不是我想要的,所以我正在使用json:"originalname"标签将它们转换为对我有意义的名称。

当我将这样的对象编组回JSON时,自然会再次得到丑陋的(原始)名称。

在编组时是否有一种忽略标签的方法?或者有没有一种指定编组和解组时使用不同名称的方法?

为了澄清,我在playground中准备了一个示例,并在下面粘贴了相同的代码。

提前感谢。

package main

import (
	"encoding/json"
	"fmt"
)

type Band struct {
	Name   string `json:"bandname"`
	Albums int    `json:"albumcount"`
}

func main() {
	// JSON -> Object
	data := []byte(`{"bandname": "AC/DC","albumcount": 10}`)
	band := &Band{}
	json.Unmarshal(data, band)

	// Object -> JSON
	str, _ := json.Marshal(band)
	fmt.Println("Actual Result: ", string(str))
	fmt.Println("Desired Result:", `{"Name": "AC/DC","Albums": 10}`)

	// Output:
	// Actual Result:  {"bandname":"AC/DC","albumcount":10}
	// Desired Result: {"Name": "AC/DC","Albums": 10}
}
英文:

I am getting JSON data from an external source. The field names in this JSON are not something I want to carry with me, so I am converting them to names that make sense to me using the json:"originalname" tags.

When I marshal such an object back to JSON, I naturally get the ugly (original) names again.

Is there a way to ignore tags when marshalling? Or a way to specify a different name for marshall and unmarshall?

To clarify, I have prepared an example in the playground and pasted the same code below.

Thanks in advance.

package main

import (
	"encoding/json"
	"fmt"
)

type Band struct {
	Name   string `json:"bandname"`
	Albums int    `json:"albumcount"`
}

func main() {
	// JSON -> Object
	data := []byte(`{"bandname": "AC/DC","albumcount": 10}`)
	band := &Band{}
	json.Unmarshal(data, band)

	// Object -> JSON
	str, _ := json.Marshal(band)
	fmt.Println("Actual Result: ", string(str))
	fmt.Println("Desired Result:", `{"Name": "AC/DC","Albums": 10}`)

	// Output:
	// Actual Result:  {"bandname":"AC/DC","albumcount":10}
	// Desired Result: {"Name": "AC/DC","Albums": 10}
}

答案1

得分: 5

你可以使用标准库中的encoding/json包来实现以下代码:

type Marshaler interface {
    MarshalJSON() ([]byte, error)
}

下面是一个示例:

type Band struct {
    Name   string `json:"bandname"`
    Albums int    `json:"albumcount"`
}

func (b Band) MarshalJSON() ([]byte, error) {
    n, _ := json.Marshal(b.Name)
    a, _ := json.Marshal(b.Albums)
    return []byte(`{"Name":` + string(n) + `,"Albums":` + string(a) + `}`), nil
}

尽管如此,这并不是一个非常好的解决方案。

英文:

You could implement

type Marshaler interface {
        MarshalJSON() ([]byte, error)
}

from the standard library's encoding/json package. Example:

type Band struct {
    Name   string `json:"bandname"`
    Albums int    `json:"albumcount"`
}

func (b Band) MarshalJSON() ([]byte, error) {
    n, _ := json.Marshal(b.Name)
    a, _ := json.Marshal(b.Albums)
    return []byte(`{"Name":` + string(n) + `,"Albums":` + string(a) + `}`)
}

It's admittedly not a very nice solution, though.

答案2

得分: 0

作为通用解决方案,您可以使用反射来创建一个新类型,该类型去除了json标签,然后进行编组。

func getVariantStructValue(v reflect.Value, t reflect.Type) reflect.Value {
    sf := make([]reflect.StructField, 0)
    for i := 0; i < t.NumField(); i++ {
        sf = append(sf, t.Field(i))

        if t.Field(i).Tag.Get("json") != "" {
            sf[i].Tag = ``
        }
    }
    newType := reflect.StructOf(sf)
    return v.Convert(newType)
}

func MarshalIgnoreTags(obj interface{}) ([]byte, error) {
    value := reflect.ValueOf(obj)
    t := value.Type()
    newValue := getVariantStructValue(value, t)
    return json.Marshal(newValue.Interface())
}

然后,您可以使用以下方式调用它:

str, _ := MarshalIgnoreTags(band)

执行相反的操作(在解组JSON时忽略标签)会有些棘手,但可以使用mapstructure实现:

func UnmarshalIgnoreTags(data []byte, obj interface{}) error {
    rv := reflect.ValueOf(obj)
    if rv.Kind() != reflect.Ptr || rv.IsNil() {
        return errors.New("unmarshal destination obj must be a non-nil pointer")
    }

    value := reflect.Indirect(rv)
    t := value.Type()

    newValue := getVariantStructValue(value, t)
    i := newValue.Interface()
    err := json.Unmarshal(data, &i)
    if err == nil {
        // 我们使用mapstructure,因为i的类型是map[string]interface{},这是将其转换回结构体类型的最简单方法
        // 参考:https://stackoverflow.com/a/38939459/2516916
        mapstructure.Decode(i, obj)
    }

    return err
}

在这里查看playground示例:https://play.golang.org/p/XVYGigM71Cf

英文:

As a generic solution, you could use reflection to create a new type that removes the json tags and then marshall that.

func getVariantStructValue(v reflect.Value, t reflect.Type) reflect.Value {
	sf := make([]reflect.StructField, 0)
	for i := 0; i &lt; t.NumField(); i++ {
		sf = append(sf, t.Field(i))

		if t.Field(i).Tag.Get(&quot;json&quot;) != &quot;&quot; {
			sf[i].Tag = ``
		}
	}
	newType := reflect.StructOf(sf)
	return v.Convert(newType)
}

func MarshalIgnoreTags(obj interface{}) ([]byte, error) {
	value := reflect.ValueOf(obj)
	t := value.Type()
	newValue := getVariantStructValue(value, t)
	return json.Marshal(newValue.Interface())
}

And you would just call it using:

str, _ := MarshalIgnoreTags(band)

Doing the opposite is a little trickier (ignore tags when unmarshalling JSON), but possible with mapstructure:

func UnmarshalIgnoreTags(data []byte, obj interface{}) error {
	rv := reflect.ValueOf(obj)
	if rv.Kind() != reflect.Ptr || rv.IsNil() {
		return errors.New(&quot;unmarshal destination obj must be a non-nil pointer&quot;)
	}

	value := reflect.Indirect(rv)
	t := value.Type()

	newValue := getVariantStructValue(value, t)
	i := newValue.Interface()
	err := json.Unmarshal(data, &amp;i)
	if err == nil {
		// We use mapstructure because i is of type map[string]interface{} and it&#39;s the easiest way to convert back to struct type
		// See: https://stackoverflow.com/a/38939459/2516916
		mapstructure.Decode(i, obj)
	}

	return err
}

See playground here: https://play.golang.org/p/XVYGigM71Cf

答案3

得分: 0

可以在这个帖子中提到的链接中使用一个库来实现这个功能:https://stackoverflow.com/a/50966527/5649638

这将给你一个类似这样的结构体:

type TestJson struct {
    Name string `json:"name" newtag:"newname"`
    Age  int    `json:"age" newtag:"newage"`
}
英文:

One could also use a library as mentioned in this thread: https://stackoverflow.com/a/50966527/5649638

This will give you a structs like this:

type TestJson struct {
    Name string `json:&quot;name&quot; newtag:&quot;newname&quot;`
    Age  int    `json:&quot;age&quot; newtag:&quot;newage&quot;`
}

huangapple
  • 本文由 发表于 2014年10月17日 22:00:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/26426746.html
匿名

发表评论

匿名网友

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

确定