일부 알려진 필드 이름과 일부 알려지지 않은 필드 이름을 가진 JSON의 마셜 해제
다음 JSON을 가지고 있습니다.
{"a":1, "b":2, "?":1, "??":1}
"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.
}
그걸 어떻게 하는 거죠?
두 번 언마샬
한 가지 옵션은 두 번 마셜을 해제하는 것입니다. 한 번을 유형 값으로 변환합니다.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)
}
{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:"-"` // 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)
}
출력은 동일합니다(Go Playground).
{A:1 B:2 X:map[x:1 y:1]}
좋은 방법은 아니지만 이 기능을 구현함으로써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
는 디코딩 중 재발을 방지하기 위해 필요합니다.
가장 간단한 방법은 다음과 같은 인터페이스를 사용하는 것입니다.
var f interface{}
s := `{"a":1, "b":2, "x":1, "y":1}`
if err := json.Unmarshal([]byte(s), &f); err != nil {
panic(err)
}
불확실한 타입의 json을 제거하기 위해 인터페이스를 사용합니다.
bytes := []byte(`{"name":"Liam","gender":1, "salary": 1}`)
var p2 interface{}
json.Unmarshal(bytes, &p2)
m := p2.(map[string]interface{})
fmt.Println(m)
거의 싱글 패스, 사용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
마시멜로가 있는 싱글 패스
우리는 정확히 그 문제를 해결하기 위해 마시멜로를 사용한다.코드를 다른 솔루션보다 깨끗하고 유지보수가 용이하도록 명시적으로 코딩할 필요는 없지만 최고의 퍼포먼스를 제공합니다(여기서 제공하는 다른 솔루션보다 최대 x3 고속, 벤치마크 및 결과 참조).
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]
}
마시멜로는 한동안 Permer X에서 내부적으로 사용되고 있으며, 최근 오픈 소스로 결정되었습니다.또, JSON의 생산 코스트의 70%를 삭감하는 데 도움이 되는 것에 대해서도 블로그에 투고했습니다.
싱글 패스, 사용github.com/ugorji/go/codec
마셜링 해제 시map
,encoding/json
지도는 비우지만ugorji/go/codec
Doesn't.또한 기존 값을 채우려고 시도하므로 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)
}
사용되지 않는 필드를 추적하는 Hashicorp의 Map-to-Recording 디코더를 사용합니다.https://godoc.org/github.com/mitchellh/mapstructure#example-Decode--Metadata
두 개의 경로로 되어 있지만 알려진 필드 이름을 어디에도 사용할 필요가 없습니다.
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]}
}
언급URL : https://stackoverflow.com/questions/33436730/unmarshal-json-with-some-known-and-some-unknown-field-names
'code' 카테고리의 다른 글
각도 UI 라우터: 상태에 대한 액세스를 방지하는 방법 (0) | 2023.02.08 |
---|---|
뷰가 열리거나 표시될 때마다 컨트롤러 기능 실행 (0) | 2023.02.08 |
Jackson이 JSON에 매핑하고 있는 개체의 일부 필드를 숨깁니다. (0) | 2023.02.08 |
RestSharp JSON 파라미터 게시 (0) | 2023.02.08 |
AngularJS 태그 속성 (0) | 2023.02.08 |