2 回答

TA貢獻(xiàn)1966條經(jīng)驗 獲得超4個贊
如果我從您的附加評論中正確理解了問題,那么輸入可能包含任何具有未知名稱(和類型?)的任意額外字段,并且您想要/需要訪問這些字段。如果只是為了以后重新編組,那么該json.RawMessage類型會很有趣。
理想情況下encoding/json會有一個特殊的標(biāo)簽(如",any" encoding/xml標(biāo)簽),它會自動將任何額外/未引用的 JSON 項目收集到 a map[string]interface{}或map[string]json.RawMessage字段中。但是我找不到任何這樣的功能,也找不到一種明顯的方法來用匿名結(jié)構(gòu)來模擬它(但我并沒有很努力地嘗試)。
編輯:Go 項目中有一個針對此功能的未解決問題。顯然,圍繞 Go 1.2 提交了一個更改并進(jìn)行了部分審查,但最終沒有被接受。
如果做不到這一點,有幾種方法可以完全按照您的建議執(zhí)行,為 X 制作自定義 (un) marshaller 并回調(diào)到 json 包以處理[]A和[]B.
這是一個快速組合在一起的示例,可能有更好/更清晰/更安全的方法來做到這一點。(在整個示例中,A 和 B 可以是任意復(fù)雜的,可能包含本身具有自定義(取消)編組方法的類型。)
package main
import (
"encoding/json"
"fmt"
)
type A struct {
AF1 string
}
type B struct {
BF1 string
}
type X struct {
Things []A
Thangs []B
// Or perhaps json.RawMessage if you just
// want to pass them through.
// Or map of string/int/etc if the value type is fixed.
Extra map[string]interface{}
}
// Marshal Way 1: call unmarshal twice on whole input
type xsub struct {
Things []A `json:"things"`
Thangs []B `json:"thangs"`
}
func (x *X) _UnmarshalJSON(b []byte) error {
// First unmarshall the known keys part:
var tmp xsub
if err := json.Unmarshal(b, &tmp); err != nil {
return err
}
// Then unmarshall the whole thing again:
var vals map[string]interface{}
if err := json.Unmarshal(b, &vals); err != nil {
return err
}
// Everything worked, chuck the map entries for
// "known" fields and store results.
delete(vals, "things")
delete(vals, "thangs")
x.Things = tmp.Things
x.Thangs = tmp.Thangs
x.Extra = vals
return nil
}
// Way 2:
func (x *X) UnmarshalJSON(b []byte) error {
// Only partially decode:
var tmp map[string]json.RawMessage
if err := json.Unmarshal(b, &tmp); err != nil {
return err
}
// Now handle the known fields:
var things []A
if err := json.Unmarshal(tmp["things"], &things); err != nil {
return err
}
var thangs []B
if err := json.Unmarshal(tmp["thangs"], &thangs); err != nil {
return err
}
// And the unknown fields.
var extra map[string]interface{}
// Either:
if true {
// this has more calls to Unmarshal, but may be more desirable
// as it completely skips over the already handled things/thangs.
delete(tmp, "things")
delete(tmp, "thangs")
// If you only needed to store the json.RawMessage for use
// in MarshalJSON then you'd just store "tmp" and stop here.
extra = make(map[string]interface{}, len(tmp))
for k, raw := range tmp {
var v interface{}
if err := json.Unmarshal(raw, &v); err != nil {
return err
}
extra[k] = v
}
} else { // Or:
// just one more call to Unmarshal, but it will waste
// time with things/thangs again.
if err := json.Unmarshal(b, &extra); err != nil {
return err
}
delete(extra, "things")
delete(extra, "thangs")
}
// no error, we can store the results
x.Things = things
x.Thangs = thangs
x.Extra = extra
return nil
}
func (x X) MarshalJSON() ([]byte, error) {
// abusing/reusing x.Extra, could copy map instead
x.Extra["things"] = x.Things
x.Extra["thangs"] = x.Thangs
result, err := json.Marshal(x.Extra)
delete(x.Extra, "things")
delete(x.Extra, "thangs")
return result, err
}
func main() {
inputs := []string{
`{"things": [], "thangs": []}`,
`
{
"things": [
{
"AF1": "foo"
},
{
"AF1": "bar"
}
],
"thangs": [
{
"BF1": "string value"
}
],
"xRandomKey": "not known ahead of time",
"xAreValueTypesKnown": 172
}`,
}
for _, in := range inputs {
fmt.Printf("\nUnmarshal(%q):\n", in)
var x X
err := json.Unmarshal([]byte(in), &x)
if err != nil {
fmt.Println("unmarshal:", err)
} else {
fmt.Printf("\tas X: %+v\n", x)
fmt.Printf("\twith map: %v\n", x.Extra)
out, err := json.Marshal(x)
if err != nil {
fmt.Println("marshal:", err)
continue
}
fmt.Printf("\tRemarshals to: %s\n", out)
}
}
}
Run on Playground

TA貢獻(xiàn)1805條經(jīng)驗 獲得超10個贊
作為 Dace C 答案的附加答案。我想實現(xiàn)與您相同的目標(biāo),但是我想重用該函數(shù)而不是對值進(jìn)行硬編碼。
這是我做的:
type DynamicFieldsUnmarshaller interface {
WithExtraFields(map[string]interface{})
Unmarshal([]byte) error
}
type TestObject struct {
Name string `json:"name"`
CustomFields map[string]interface{} `json:"-"`
}
func (o *TestObject) Unmarshal(data []byte) error {
return UnmarshalCustomJSON(data,o)
}
func (o *TestObject) WithExtraFields(f map[string]interface{}) {
o.CustomFields = f
}
func UnmarshalCustomJSON(b []byte, o DynamicFieldsUnmarshaller) error {
if err := json.Unmarshal(b, &o); err != nil {
return err
}
// unmarshal everything to a map
var vals map[string]interface{}
if err := json.Unmarshal(b, &vals); err != nil {
return err
}
if len(vals)== 0 {
return nil
}
fields := reflect.TypeOf(o).Elem()
num := fields.NumField()
for i := 0; i < num; i++ {
field := fields.Field(i)
jsonTag := field.Tag.Get("json")
if jsonTag != "" && jsonTag != "-" {
delete(vals, jsonTag)
}
}
o.WithExtraFields(vals)
return nil
}
這應(yīng)該只將不在結(jié)構(gòu)中的值添加到map[string]interface{}字段中。
例如:
body := []byte(`
{
"name":"kilise",
"age": 40
}
`)
var dto TestObject
err := dto.Unmarshal(body)
if err != nil {
panic(err)
}
只會將“年齡”添加到 dto.CustomFields 地圖。
請注意,此解決方案可能并不總是最好的,因為它沒有實現(xiàn) json.Unmarshaler
- 2 回答
- 0 關(guān)注
- 182 瀏覽
添加回答
舉報