为了账号安全,请及时绑定邮箱和手机立即绑定

我如何在切片内对切片进行 json 解组

我如何在切片内对切片进行 json 解组

Go
动漫人物 2023-03-21 17:25:49
我正在尝试解组一些非常丑陋的 json,但不知道如何解组。我有:package mainimport "fmt"import "encoding/json"type PublicKey struct {    ID     int    `json:"id"`    Key    string `json:"key"`    MyData []struct {        ID    string `json:"id"`        Value int    `json:"value"`    }}func main() {    b := `[  {    "id": 1,    "key": "my_key"  },  [    {      "id": "some_id",      "value": 12    },    {      "id": "anorther_id",      "value": 13    }  ]]`    var pk []PublicKey    err := json.Unmarshal([]byte(b), &pk)    if err != nil {        fmt.Println(err)    }    fmt.Println(pk)}对于我得到的结果:[{1 my_key []} {0  []}]第二个切片本不应该是空的。编辑:我得到的错误是:json: cannot unmarshal array into Go struct field PublicKey.key of type main.PublicKeyhttps://play.golang.org/p/cztXOchiiS5
查看完整描述

2 回答

?
梵蒂冈之花

TA贡献1900条经验 获得超5个赞

那是一些真正可怕的 JSON!我有两种方法来处理混合数组元素,我更喜欢第二种。interface这是使用类型开关的第一种方法:


package main


import (

    "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 main


import (

    "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


查看完整回答
反对 回复 2023-03-21
?
大话西游666

TA贡献1817条经验 获得超14个赞

这是一种方法,它结合了在通过创建一个新的临时类型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}}}


}


查看完整回答
反对 回复 2023-03-21
  • 2 回答
  • 0 关注
  • 98 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信