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

在go中解码嵌套的json对象

在go中解码嵌套的json对象

Go
MYYA 2021-10-04 17:14:24
我找到了一些关于如何在 go 中解码 json 嵌套对象的帖子,我尝试将答案应用于我的问题,但我只找到了部分解决方案。我的 json 文件如下所示:{"user":{    "gender":"male",    "age":"21-30",    "id":"80b1ea88-19d7-24e8-52cc-65cf6fb9b380"    },"trials":{    "0":{"index":0,"word":"WORD 1","Time":3000,"keyboard":true,"train":true,"type":"A"},    "1":{"index":1,"word":"WORD 2","Time":3000,"keyboard":true,"train":true,"type":"A"},    },"answers":{    "training":[        {"ans":0,"RT":null,"gtAns":"WORD 1","correct":0},        {"ans":0,"RT":null,"gtAns":"WORD 2","correct":0}        ],    "test":[        {"ans":0,"RT":null,"gtAns":true,"correct":0},        {"ans":0,"RT":null,"gtAns":true,"correct":0}        ]    }}基本上我需要解析其中的信息并将它们保存到 go 结构中。使用下面的代码,我设法提取了用户信息,但对我来说它看起来太复杂了,将同样的事情应用到“答案”字段并不容易,该字段包含 2 个数组,每个数组超过 100 个条目。这是我现在使用的代码:type userDetails struct {    Id     string `json:"id"`    Age    string `json:"age"`    Gender string `json:"gender"`}type jsonRawData map[string]interface {}func getJsonContent(r *http.Request) ( userDetails) {    defer r.Body.Close()    jsonBody, err := ioutil.ReadAll(r.Body)    var userDataCurr userDetails    if err != nil {        log.Printf("Couldn't read request body: %s", err)    } else {        var f jsonRawData        err := json.Unmarshal(jsonBody, &f)        if err != nil {            log.Printf("Error unmashalling: %s", err)        } else {            user := f["user"].(map[string]interface{})            userDataCurr.Id = user["id"].(string)            userDataCurr.Gender = user["gender"].(string)            userDataCurr.Age = user["age"].(string)        }    }    return userDataCurr}有什么建议?非常感谢!
查看完整描述

1 回答

?
Helenr

TA贡献1780条经验 获得超4个赞

你通过使用interface{}而不是利用encoding/json给你的东西来做这件事。


我会做这样的事情(注意我假设“gtAns”字段的类型有错误,我把它设为布尔值,你没有提供足够的信息来知道如何处理“RT”字段):


package main


import (

        "encoding/json"

        "fmt"

        "io"

        "log"

        "strconv"

        "strings"

)


const input = `{

"user":{

    "gender":"male",

    "age":"21-30",

    "id":"80b1ea88-19d7-24e8-52cc-65cf6fb9b380"

    },

"trials":{

    "0":{"index":0,"word":"WORD 1","Time":3000,"keyboard":true,"train":true,"type":"A"},

    "1":{"index":1,"word":"WORD 2","Time":3000,"keyboard":true,"train":true,"type":"A"}

    },

"answers":{

    "training":[

        {"ans":0,"RT":null,"gtAns":true,"correct":0},

        {"ans":0,"RT":null,"gtAns":true,"correct":0}

        ],

    "test":[

        {"ans":0,"RT":null,"gtAns":true,"correct":0},

        {"ans":0,"RT":null,"gtAns":true,"correct":0}

        ]

    }

}`


type Whatever struct {

        User struct {

                Gender Gender   `json:"gender"`

                Age    Range    `json:"age"`

                ID     IDString `json:"id"`

        } `json:"user"`

        Trials map[string]struct {

                Index int    `json:"index"`

                Word  string `json:"word"`

                Time  int    // should this be a time.Duration?

                Train bool   `json:"train"`

                Type  string `json:"type"`

        } `json:"trials"`

        Answers map[string][]struct {

                Answer    int             `json:"ans"`

                RT        json.RawMessage // ??? what type is this

                GotAnswer bool            `json:"gtAns"`

                Correct   int             `json:"correct"`

        } `json:"answers"`

}


// Using some custom types to show custom marshalling:


type IDString string // TODO custom unmarshal and format/error checking


type Gender int


const (

        Male Gender = iota

        Female

)


func (g *Gender) UnmarshalJSON(b []byte) error {

        var s string

        err := json.Unmarshal(b, &s)

        if err != nil {

                return err

        }

        switch strings.ToLower(s) {

        case "male":

                *g = Male

        case "female":

                *g = Female

        default:

                return fmt.Errorf("invalid gender %q", s)

        }

        return nil

}

func (g Gender) MarshalJSON() ([]byte, error) {

        switch g {

        case Male:

                return []byte(`"male"`), nil

        case Female:

                return []byte(`"female"`), nil

        default:

                return nil, fmt.Errorf("invalid gender %v", g)

        }

}


type Range struct{ Min, Max int }


func (r *Range) UnmarshalJSON(b []byte) error {

        // XXX could be improved

        _, err := fmt.Sscanf(string(b), `"%d-%d"`, &r.Min, &r.Max)

        return err

}

func (r Range) MarshalJSON() ([]byte, error) {

        return []byte(fmt.Sprintf(`"%d-%d"`, r.Min, r.Max)), nil

        // Or:

        b := make([]byte, 0, 8)

        b = append(b, '"')

        b = strconv.AppendInt(b, int64(r.Min), 10)

        b = append(b, '-')

        b = strconv.AppendInt(b, int64(r.Max), 10)

        b = append(b, '"')

        return b, nil

}


func fromJSON(r io.Reader) (Whatever, error) {

        var x Whatever

        dec := json.NewDecoder(r)

        err := dec.Decode(&x)

        return x, err

}


func main() {

        // Use http.Get or whatever to get an io.Reader,

        // (e.g. response.Body).

        // For playground, substitute a fixed string

        r := strings.NewReader(input)


        // If you actually had a string or []byte:

        //     var x Whatever

        //     err := json.Unmarshal([]byte(input), &x)


        x, err := fromJSON(r)

        if err != nil {

                log.Fatal(err)

        }

        fmt.Println(x)

        fmt.Printf("%+v\n", x)


        b, err := json.MarshalIndent(x, "", "  ")

        if err != nil {

                log.Fatal(err)

        }

        fmt.Printf("Re-marshalled: %s\n", b)


}

Playground


当然,如果您想重用这些子类型,您可以将它们从“Whatever”类型中提取到它们自己的命名类型中。


另外,请注意使用 ajson.Decoder而不是提前读取所有数据。通常尽量避免使用 ,ioutil.ReadAll除非您真的需要一次获得所有数据。


查看完整回答
反对 回复 2021-10-04
  • 1 回答
  • 0 关注
  • 265 浏览
慕课专栏
更多

添加回答

举报

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