梵蒂冈之花
那是一些真正可怕的 JSON!我有两种方法来处理混合数组元素,我更喜欢第二种。interface这是使用类型开关的第一种方法:package mainimport ( "encoding/json" "errors" "fmt")type PublicKey struct { ID int `json:"id"` Key string `json:"key"`}type MyData struct { ID string `json:"id"` Value int `json:"value"`}type MixedData struct { Key []PublicKey MyData [][]MyData}func (md *MixedData) UnmarshalJSON(b []byte) error { md.Key = []PublicKey{} md.MyData = [][]MyData{} var obj []interface{} err := json.Unmarshal([]byte(b), &obj) if err != nil { return err } for _, o := range obj { switch o.(type) { case map[string]interface{}: m := o.(map[string]interface{}) id, ok := m["id"].(float64) if !ok { return errors.New("public key id must be an int") } pk := PublicKey{} pk.ID = int(id) pk.Key, ok = m["key"].(string) if !ok { return errors.New("public key key must be a string") } md.Key = append(md.Key, pk) case []interface{}: a := o.([]interface{}) myData := make([]MyData, len(a)) for i, x := range a { m, ok := x.(map[string]interface{}) if !ok { return errors.New("data array contains unexpected object") } val, ok := m["value"].(float64) if !ok { return errors.New("data value must be an int") } myData[i].Value = int(val) myData[i].ID, ok = m["id"].(string) if !ok { return errors.New("data id must be a string") } md.MyData = append(md.MyData, myData) } default: // got something unexpected, handle somehow } } return nil}func main() { b := `[ { "id": 1, "key": "my_key" }, [ { "id": "some_id", "value": 12 }, { "id": "another_id", "value": 13 } ]]` m := MixedData{} err := json.Unmarshal([]byte(b), &m) if err != nil { fmt.Println(err) } fmt.Println(m)}https://play.golang.org/p/g8d_AsH-pYY希望没有任何意外的其他元素,但它们可以类似地处理。这是第二个更多地依赖于 Go 的内部 JSON 解析的帮助json.RawMessage。它对数组的内容做出相同的假设。它假定任何对象都将解组为PublicKey实例,并且任何数组仅由实例组成MyData。我还添加了如何编组回目标 JSON 以实现对称:package mainimport ( "encoding/json" "fmt" "os")type PublicKey struct { ID int `json:"id"` Key string `json:"key"`}type MyData struct { ID string `json:"id"` Value int `json:"value"`}type MixedData struct { Keys []PublicKey MyData [][]MyData}func (md *MixedData) UnmarshalJSON(b []byte) error { md.Keys = []PublicKey{} md.MyData = [][]MyData{} obj := []json.RawMessage{} err := json.Unmarshal([]byte(b), &obj) if err != nil { return err } for _, o := range obj { switch o[0] { case '{': pk := PublicKey{} err := json.Unmarshal(o, &pk) if err != nil { return err } md.Keys = append(md.Keys, pk) case '[': myData := []MyData{} err := json.Unmarshal(o, &myData) if err != nil { return err } md.MyData = append(md.MyData, myData) default: // got something unexpected, handle somehow } } return nil}func (md *MixedData) MarshalJSON() ([]byte, error) { out := make([]interface{}, len(md.Keys)+len(md.MyData)) i := 0 for _, x := range md.Keys { out[i] = x i++ } for _, x := range md.MyData { out[i] = x i++ } return json.Marshal(out)}func main() { b := `[ { "id": 1, "key": "my_key" }, [ { "id": "some_id", "value": 12 }, { "id": "another_id", "value": 13 } ]]` m := MixedData{} err := json.Unmarshal([]byte(b), &m) if err != nil { fmt.Println(err) os.Exit(1) } fmt.Println(m) enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") if err := enc.Encode(m); err != nil { fmt.Println(err) os.Exit(1) }}https://play.golang.org/p/ryZzaWKNcN0
大话西游666
这是一种方法,它结合了在通过创建一个新的临时类型json.RawMessage来实现的类型中使用默认解组器的技巧json.Unmarshaler,该临时类型为目标类型设置别名。我们的想法是,我们将传入的数组解组为原始消息,并确保数组长度符合我们的预期。然后我们使用它们的 JSON 标记注释将各个数组元素解组为自定义结构类型。最终结果是我们可以用PublicKey通常的方式解组类型,UnmarshalJSON一旦你理解了这些技巧,代码就不会很难理解了。例如(去游乐场):type PublicKey struct { ID int `json:"id"` Key string `json:"key"` Data []MyData}type MyData struct { ID string `json:"id"` Value int `json:"value"`}func (pk *PublicKey) UnmarshalJSON(bs []byte) error { // Unmarshal into a RawMessage so we can inspect the array length. var rawMessage []json.RawMessage err := json.Unmarshal(bs, &rawMessage) if err != nil { return err } if len(rawMessage) != 2 { return fmt.Errorf("expected array of length 2, got %d", len(rawMessage)) } // Parse the first object as PublicKey using the default unmarshaler // using a temporary type that is an alias for the target type. type PublicKey2 PublicKey var pk2 PublicKey2 err = json.Unmarshal(rawMessage[0], &pk2) if err != nil { return err } // Parse the second object as []MyData in the usual way. err = json.Unmarshal(rawMessage[1], &pk2.Data) if err != nil { return err } // Finally, assign the aliased object to the target object. *pk = PublicKey(pk2) return nil}func main() { var pk PublicKey err := json.Unmarshal([]byte(jsonstr), &pk) if err != nil { panic(err) } fmt.Printf("%#v\n", pk) // main.PublicKey{ID:1, Key:"my_key", Data:[]main.MyData{main.MyData{ID:"some_id", Value:12}, main.MyData{ID:"anorther_id", Value:13}}}}