英文:
What's the best way to maintain un-parsed JSON fields in Go?
问题
我想将一个 JSON 数据块解码为 Go 结构体,对其进行操作,然后将其重新编码为 JSON。然而,JSON 中有一些动态字段与我的结构体无关,我希望在序列化回 JSON 时保留它们。
例如:
{ "name": "Joe Smith",
"age": 42,
"phone": "614-555-1212",
"debug": true,
"codeword": "wolf" }
type Person struct {
Name string
Age uint
Phone string
}
var p Person
json.Unmarshal(data, &p)
// 生日快乐
p.Age++
data, _ = json.Marshal(p)
// 有没有办法保留 "debug" 和 "codeword" 字段,这些字段可能事先不知道?
我知道一种可能性是将所有内容解码为 map[string]interface{}
,但是当你这样做时,事情会变得很混乱。
有没有办法兼顾两全呢?
英文:
I'd like to decode a JSON blob into a Go struct, manipulate on it, and encode it back to JSON. However, there are dynamic fields in the JSON that aren't relevant to my struct and I want to maintain them when I serialize back to JSON.
For example:
{ "name": "Joe Smith",
"age": 42,
"phone": "614-555-1212",
"debug": True,
"codeword": "wolf" }
type Person struct {
Name string
Age uint
Phone string
}
var p Person
json.Unmarshal(data, &p)
// Happy birthday
p.Age++
data, _ = json.Marshal(p)
// Any way to maintain the "debug" and "codeword" fields -- which might not
// be known ahead of time?
I know one possibility is to decode everything into a map[string]interface{}
but boy, do things get ugly when you do that.
Is there any way to have the best of both worlds?
答案1
得分: 7
使用encoding/json
包,无法同时解码结构体并将未知字段保存在同一级别以供后续重新编码。你可以通过使用json.RawMessage
类型选择不解码结构体的一部分来解决这个问题,示例如下:
type Person struct {
Name string
Address json.RawMessage
}
你可以通过实现自己的Unmarshaler
来解决这个问题,该Unmarshaler
将文档解码为一个映射,并将未知的键保存在结构体的一个字段中,然后再实现一个相应的Marshaler
,在编组之前将字段放回去。
只是出于好奇,你所寻找的功能在labix.org/v2/mgo/bson
中是存在的,通过使用inline tag flag,目标正是解决你所描述的用例。
英文:
With encoding/json
there's no way to do decode a struct and save unknown fields at the same level for posterior re-encoding. What you can do is choose not to decode part of the struct by using the json.RawMessage type, along the lines of:
type Person struct {
Name string
Address json.RawMessage
}
You might workaround that by implementing your own Unmarshaler that decodes the document into a map and saves the unknown keys in a field of the struct, and then have a counterpart Marshaler that puts the fields back before marshaling.
Just out of curiosity, the feature you're looking for does exist in labix.org/v2/mgo/bson, via the inline tag flag, and the goal was to solve precisely the use case you're describing.
答案2
得分: 3
原来我写了自己的库来实现这个功能:https://github.com/joeshaw/json-lossless
它是在go-simplejson的基础上构建的,将解析的JSON状态保存在simplejson.Json
中,并在结构体在进行编组或解组时在它和结构体之间代理状态。
示例用法:
package main
import (
"encoding/json"
"fmt"
"time"
"github.com/joeshaw/json-lossless"
)
type Person struct {
lossless.JSON `json:"-"`
Name string `json:"name"`
Age int `json:"age"`
Birthdate time.Time `json:"birthdate"`
}
func (p *Person) UnmarshalJSON(data []byte) error {
return p.JSON.UnmarshalJSON(p, data)
}
func (p Person) MarshalJSON() ([]byte, error) {
return p.JSON.MarshalJSON(p)
}
var jsondata = []byte(`
{"name": "David Von Wolf",
"age": 33,
"birthdate": "1980-09-16T10:44:40.295451647-04:00",
"phone": "614-555-1212"}
`)
func main() {
var p Person
err := json.Unmarshal(jsondata, &p)
if err != nil {
panic(err)
}
// 在结构体上设置值
p.Age++
// 在结构体中设置不在结构体中的任意键
p.Set("title", "Chief Wolf")
fmt.Printf("%#v\n", p)
data, err := json.Marshal(p)
if err != nil {
panic(err)
}
fmt.Println(string(data))
}
输出结果(由我格式化以便阅读):
main.Person{JSON:lossless.JSON{json:(*simplejson.Json)(0x21020a190)},
Name:"David Von Wolf",
Age:34,
Birthdate:time.Time{sec:62473560280,
nsec:295451647,
loc:(*time.Location)(0x16de60)}}
{"age":34,
"birthdate":"1980-09-16T10:44:40.295451647-04:00",
"name":"David Von Wolf",
"phone":"614-555-1212",
"title": "Chief Wolf"}
英文:
Turns out I wrote my own library to do this: https://github.com/joeshaw/json-lossless
It builds on top of go-simplejson, keeping the parsed JSON state in a simplejson.Json
and proxying state between it and the struct whenever the struct is marshaled or unmarshaled.
Example usage:
package main
import (
"encoding/json"
"fmt"
"time"
"github.com/joeshaw/json-lossless"
)
type Person struct {
lossless.JSON `json:"-"`
Name string `json:"name"`
Age int `json:"age"`
Birthdate time.Time `json:"birthdate"`
}
func (p *Person) UnmarshalJSON(data []byte) error {
return p.JSON.UnmarshalJSON(p, data)
}
func (p Person) MarshalJSON() []byte, error) {
return p.JSON.MarshalJSON(p)
}
var jsondata = []byte(`
{"name": "David Von Wolf",
"age": 33,
"birthdate": "1980-09-16T10:44:40.295451647-04:00",
"phone": "614-555-1212"}
`)
func main() {
var p Person
err := json.Unmarshal(jsondata, &p)
if err != nil {
panic(err)
}
// Set values on the struct
p.Age++
// Set arbitrary keys not in the struct
p.Set("title", "Chief Wolf")
fmt.Printf("%#v\n", p)
data, err := json.Marshal(p)
if err != nil {
panic(err)
}
fmt.Println(string(data))
}
Prints (formatted for readability by me):
main.Person{JSON:lossless.JSON{json:(*simplejson.Json)(0x21020a190)},
Name:"David Von Wolf",
Age:34,
Birthdate:time.Time{sec:62473560280,
nsec:295451647,
loc:(*time.Location)(0x16de60)}}
{"age":34,
"birthdate":"1980-09-16T10:44:40.295451647-04:00",
"name":"David Von Wolf",
"phone":"614-555-1212",
"title": "Chief Wolf"}
答案3
得分: 1
go-simplejson
这个包非常适合这种类型的工作。
英文:
Package go-simplejson
comes handy for this kind of jobs.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论