英文:
Unmarshal JSON with some known, and some unknown field names
问题
你好!要将未知字段解析为map[string]interface{}类型,你可以使用json.RawMessage作为中间类型。以下是一个示例代码:
import (
"encoding/json"
"fmt"
)
type Foo struct {
A int `json:"a"`
B int `json:"b"`
X map[string]interface{} `json:"-"`
}
func (f *Foo) UnmarshalJSON(data []byte) error {
type Alias Foo
aux := &struct {
X json.RawMessage `json:"-"`
*Alias
}{
Alias: (*Alias)(f),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
if err := json.Unmarshal(aux.X, &f.X); err != nil {
return err
}
return nil
}
func main() {
data := []byte(`{"a":1, "b":2, "?":1, "??":1}`)
var foo Foo
if err := json.Unmarshal(data, &foo); err != nil {
fmt.Println(err)
return
}
fmt.Println(foo)
}
在这个示例中,我们定义了一个Foo结构体,其中A和B是已知字段,X是未知字段的map。在UnmarshalJSON方法中,我们使用json.RawMessage作为中间类型来解析未知字段。首先,我们将整个JSON数据解析到一个辅助结构体中,其中X字段使用json.RawMessage类型来存储未知字段的原始JSON数据。然后,我们将aux.X解析为map[string]interface{}类型,并将结果存储在Foo结构体的X字段中。
希望这可以帮助到你!如果你有任何其他问题,请随时问我。
英文:
I have the following 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:
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?
答案1
得分: 71
反序列化两次
一种选项是进行两次反序列化:一次将其解析为类型为Foo
的值,一次将其解析为类型为map[string]interface{}
的值,并删除键"a"
和"b"
:
type Foo struct {
A int `json:"a"`
B int `json:"b"`
X map[string]interface{} `json:"-"`
}
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)
}
输出结果(在Go Playground上尝试):
{A:1 B:2 X:map[x:1 y:1]}
反序列化一次并手动处理
另一种选项是将其一次反序列化为map[string]interface{}
,并手动处理Foo.A
和Foo.B
字段:
type Foo struct {
A int `json:"a"`
B int `json:"b"`
X map[string]interface{} `json:"-"`
}
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)
}
输出结果与上述方法相同(在Go Playground上尝试):
{A:1 B:2 X:map[x:1 y:1]}
英文:
Unmarshal twice
One option is to unmarshal twice: once into a value of type Foo
and once into a value of type map[string]interface{}
and removing the keys "a"
and "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):
{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.A
and Foo.B
fields manually:
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):
{A:1 B:2 X:map[x:1 y:1]}
答案2
得分: 28
这不是很好,但你可以通过实现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
}
类型_Foo
的存在是为了在解码时避免递归。
英文:
It's not nice, but you could to it by implementing 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 _Foo
is necessary to avoid recursion while decoding.
答案3
得分: 23
最简单的方法是使用这样的接口:
var f interface{}
s := `{"a":1, "b":2, "x":1, "y":1}`
if err := json.Unmarshal([]byte(s), &f); err != nil {
panic(err)
}
英文:
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)
}
答案4
得分: 19
我使用接口来解析不确定类型的 JSON。
bytes := []byte(`{"name":"Liam","gender":1, "salary": 1}`)
var p2 interface{}
json.Unmarshal(bytes, &p2)
m := p2.(map[string]interface{})
fmt.Println(m)
英文:
I use interface to unmarshal uncertain type json.
bytes := []byte(`{"name":"Liam","gender":1, "salary": 1}`)
var p2 interface{}
json.Unmarshal(bytes, &p2)
m := p2.(map[string]interface{})
fmt.Println(m)
答案5
得分: 17
几乎是单次遍历,使用json.RawMessage
。
我们可以将其解组为map[string]json.RawMessage
,然后逐个解组每个字段。
JSON 将被分词两次,但这是非常廉价的。
可以使用以下辅助函数:
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
}
这是完整的 Go Playground 代码 - http://play.golang.org/p/EtkJUzMmKt
英文:
Almost single pass, uses json.RawMessage
We can unmarshal into map[string]json.RawMessage
, and then unmarshal each field separately.
JSON will be tokenized twice, but that's quite cheap.
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
答案6
得分: 9
单次通过使用Marshmallow
我们使用marshmallow来解决这个问题。它不需要任何显式的编码,这使得你的代码比其他解决方案更清晰、更易于维护,同时它还提供了最佳的性能(比其他解决方案快3倍,详见仓库中的基准测试和结果)。
type Foo struct {
A int `json:"a"`
B int `json:"b"`
}
func main() {
s := `{"a":1, "b":2, "x":1, "y":1}`
f := Foo{}
result, err := marshmallow.Unmarshal([]byte(s), &f)
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", f) // {A:1 B:2}
fmt.Printf("%+v\n", result) // map[a:1 b:2 x:1 y:1]
}
Marshmallow在PerimeterX内部使用已有一段时间,我们最近决定将其开源。我们还撰写了一篇博文,介绍了它如何帮助我们在生产环境中减少70%的JSON解析成本。
英文:
Single Pass With Marshmallow
We use marshmallow to solve exactly that problem. It requires no explicit coding of any kind which keeps your code cleaner and more maintainable than other solutions, but it also provides the best performance (up to x3 faster than other solutions provided here, see benchmarks and results in the repo).
type Foo struct {
A int `json:"a"`
B int `json:"b"`
}
func main() {
s := `{"a":1, "b":2, "x":1, "y":1}`
f := Foo{}
result, err := marshmallow.Unmarshal([]byte(s), &f)
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", f) // {A:1 B:2}
fmt.Printf("%+v\n", result) // map[a:1 b:2 x:1 y:1]
}
Marshmallow is used internally at PerimeterX for some time and we've recently decided to open-source it. We also wrote a blog post about how it helped us trim 70% of our JSON parsing costs in production.
答案7
得分: 6
单次遍历,使用github.com/ugorji/go/codec
当将数据解组为map
时,encoding/json
会清空该map,但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)
}
英文:
Single pass, use github.com/ugorji/go/codec
When unmarshaling into a map
, encoding/json
empties the map, but ugorji/go/codec
doesn't. It also attempts to fill existing values, so we can put pointers to foo.A, foo.B into 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)
}
答案8
得分: 5
使用Hashicorp的map-to-struct解码器,它会跟踪未使用的字段:https://godoc.org/github.com/mitchellh/mapstructure#example-Decode--Metadata
这是一个两步操作,但你不需要在任何地方使用已知的字段名。
func UnmarshalJson(input []byte, result interface{}) (map[string]interface{}, error) {
// 将json解码为map
foomap := make(map[string]interface{})
json.Unmarshal(input, &foomap)
// 创建一个mapstructure解码器
var md mapstructure.Metadata
decoder, err := mapstructure.NewDecoder(
&mapstructure.DecoderConfig{
Metadata: &md,
Result: result,
})
if err != nil {
return nil, err
}
// 将解码后的map解码到给定的结构体中
if err := decoder.Decode(foomap); err != nil {
return nil, err
}
// 复制并返回未使用的字段
unused := map[string]interface{}{}
for _, k := range md.Unused {
unused[k] = foomap[k]
}
return unused, nil
}
type Foo struct {
// 已知字段
A int
B int
// 未知字段
X map[string]interface{} // 其余字段应放在这里
}
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) // 输出 {1 2 map[?:3 ??:4]}
}
英文:
Use Hashicorp's map-to-struct decoder, which keeps track of unused fields: 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]}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论