使用一些已知和一些未知的字段名称解组 JSON
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/33436730/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Unmarshal JSON with some known, and some unknown field names
提问by Abyx
I have the following JSON
我有以下 JSON
{"a":1, "b":2, "?":1, "??":1}
I know that it has the "a" and "b" fields, but I don't know the names of other fields. So I want to unmarshal it in the following type:
我知道它有“a”和“b”字段,但我不知道其他字段的名称。所以我想用以下类型解组它:
type Foo struct {
// Known fields
A int `json:"a"`
B int `json:"b"`
// Unknown fields
X map[string]interface{} `json:???` // Rest of the fields should go here.
}
How do I do that?
我怎么做?
回答by icza
Unmarshal twice
解组两次
One option is to unmarshal twice: once into a value of type Fooand once into a value of type map[string]interface{}and removing the keys "a"and "b":
一种选择是解组两次:一次进入一个类型的值,Foo一次进入一个类型的值map[string]interface{}并删除键"a"和"b":
type Foo struct {
A int `json:"a"`
B int `json:"b"`
X map[string]interface{} `json:"-"` // Rest of the fields should go here.
}
func main() {
s := `{"a":1, "b":2, "x":1, "y":1}`
f := Foo{}
if err := json.Unmarshal([]byte(s), &f); err != nil {
panic(err)
}
if err := json.Unmarshal([]byte(s), &f.X); err != nil {
panic(err)
}
delete(f.X, "a")
delete(f.X, "b")
fmt.Printf("%+v", f)
}
Output (try it on the Go Playground):
输出(在Go Playground上试试):
{A:1 B:2 X:map[x:1 y:1]}
Unmarshal once and manual handling
解组一次和手动处理
Another option is to unmarshal once into an map[string]interface{}and handle the Foo.Aand Foo.Bfields manually:
另一种选择是将一次解组到 anmap[string]interface{}并手动处理Foo.AandFoo.B字段:
type Foo struct {
A int `json:"a"`
B int `json:"b"`
X map[string]interface{} `json:"-"` // Rest of the fields should go here.
}
func main() {
s := `{"a":1, "b":2, "x":1, "y":1}`
f := Foo{}
if err := json.Unmarshal([]byte(s), &f.X); err != nil {
panic(err)
}
if n, ok := f.X["a"].(float64); ok {
f.A = int(n)
}
if n, ok := f.X["b"].(float64); ok {
f.B = int(n)
}
delete(f.X, "a")
delete(f.X, "b")
fmt.Printf("%+v", f)
}
Output is the same (Go Playground):
输出相同(Go Playground):
{A:1 B:2 X:map[x:1 y:1]}
回答by 0x434D53
It's not nice, but you could to it by implementing Unmarshaler:
这不好,但你可以通过实施Unmarshaler:
type _Foo Foo
func (f *Foo) UnmarshalJSON(bs []byte) (err error) {
foo := _Foo{}
if err = json.Unmarshal(bs, &foo); err == nil {
*f = Foo(foo)
}
m := make(map[string]interface{})
if err = json.Unmarshal(bs, &m); err == nil {
delete(m, "a")
delete(m, "b")
f.X = m
}
return err
}
The type _Foois necessary to avoid recursion while decoding.
该类型_Foo对于在解码时避免递归是必要的。
回答by Ariel Monaco
Simplest way is to use an interface like this:
最简单的方法是使用这样的接口:
var f interface{}
s := `{"a":1, "b":2, "x":1, "y":1}`
if err := json.Unmarshal([]byte(s), &f); err != nil {
panic(err)
}
回答by Abyx
Almost single pass, uses json.RawMessage
几乎单程,使用 json.RawMessage
We can unmarshal into map[string]json.RawMessage, and then unmarshal each field separately.
我们可以解组成map[string]json.RawMessage,然后分别解组每个字段。
JSON will be tokenized twice, but that's quite cheap.
JSON 将被标记两次,但这非常便宜。
The following helper function can be used:
可以使用以下辅助函数:
func UnmarshalJsonObject(jsonStr []byte, obj interface{}, otherFields map[string]json.RawMessage) (err error) {
objValue := reflect.ValueOf(obj).Elem()
knownFields := map[string]reflect.Value{}
for i := 0; i != objValue.NumField(); i++ {
jsonName := strings.Split(objValue.Type().Field(i).Tag.Get("json"), ",")[0]
knownFields[jsonName] = objValue.Field(i)
}
err = json.Unmarshal(jsonStr, &otherFields)
if err != nil {
return
}
for key, chunk := range otherFields {
if field, found := knownFields[key]; found {
err = json.Unmarshal(chunk, field.Addr().Interface())
if err != nil {
return
}
delete(otherFields, key)
}
}
return
}
Here is the complete code on Go Playground - http://play.golang.org/p/EtkJUzMmKt
这是 Go Playground 上的完整代码 - http://play.golang.org/p/EtkJUzMmKt
回答by LiamHsia
I use interface to unmarshal uncertain type json.
我使用接口来解组不确定类型的 json。
bytes := []byte(`{"name":"Liam","gender":1, "salary": 1}`)
var p2 interface{}
json.Unmarshal(bytes, &p2)
m := p2.(map[string]interface{})
fmt.Println(m)
回答by Circuit in the wall
Use Hashicorp's map-to-struct decoder, which keeps track of unused fields: https://godoc.org/github.com/mitchellh/mapstructure#example-Decode--Metadata
使用 Hashicorp 的 map-to-struct 解码器,它会跟踪未使用的字段:https://godoc.org/github.com/mitchellh/mapstructure#example-Decode--Metadata
It's two-pass, but you don't have to use known field names anywhere.
这是两次传递,但您不必在任何地方使用已知的字段名称。
func UnmarshalJson(input []byte, result interface{}) (map[string]interface{}, error) {
// unmarshal json to a map
foomap := make(map[string]interface{})
json.Unmarshal(input, &foomap)
// create a mapstructure decoder
var md mapstructure.Metadata
decoder, err := mapstructure.NewDecoder(
&mapstructure.DecoderConfig{
Metadata: &md,
Result: result,
})
if err != nil {
return nil, err
}
// decode the unmarshalled map into the given struct
if err := decoder.Decode(foomap); err != nil {
return nil, err
}
// copy and return unused fields
unused := map[string]interface{}{}
for _, k := range md.Unused {
unused[k] = foomap[k]
}
return unused, nil
}
type Foo struct {
// Known fields
A int
B int
// Unknown fields
X map[string]interface{} // Rest of the fields should go here.
}
func main() {
s := []byte(`{"a":1, "b":2, "?":3, "??":4}`)
var foo Foo
unused, err := UnmarshalJson(s, &foo)
if err != nil {
panic(err)
}
foo.X = unused
fmt.Println(foo) // prints {1 2 map[?:3 ??:4]}
}
回答by Abyx
Single pass, use github.com/ugorji/go/codec
单程,使用 github.com/ugorji/go/codec
When unmarshaling into a map, encoding/jsonempties the map, but ugorji/go/codecdoesn't. It also attempts to fill existing values, so we can put pointers to foo.A, foo.B into foo.X:
当解组为 a 时map,encoding/json清空地图,但ugorji/go/codec不会。它还尝试填充现有值,因此我们可以将指向 foo.A、foo.B 的指针放入 foo.X:
package main
import (
"fmt"
"github.com/ugorji/go/codec"
)
type Foo struct {
A int
B int
X map[string]interface{}
}
func (this *Foo) UnmarshalJSON(jsonStr []byte) (err error) {
this.X = make(map[string]interface{})
this.X["a"] = &this.A
this.X["b"] = &this.B
return codec.NewDecoderBytes(jsonStr, &codec.JsonHandle{}).Decode(&this.X)
}
func main() {
s := `{"a":1, "b":2, "x":3, "y":[]}`
f := &Foo{}
err := codec.NewDecoderBytes([]byte(s), &codec.JsonHandle{}).Decode(f)
fmt.Printf("err = %v\n", err)
fmt.Printf("%+v\n", f)
}

