英文:
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 < 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())
}
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("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 {
// We use mapstructure because i is of type map[string]interface{} and it'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:"name" newtag:"newname"`
Age int `json:"age" newtag:"newage"`
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论