你如何在 Golang 中修改这个结构来接受两个不同的结果?

对于以下 JSON 响应{"table_contents":[{"id":100,"description":"text100"},{"id":101,"description":"text101"},{"id":1,"description":"text1"}]}


您所要做的就是生成以下代码以正确执行它并能够从结构中读取字段,例如:


package main


import (

    "fmt"

    "encoding/json"

)


type MyStruct1 struct {

    TableContents []struct {

        ID          int

        Description string

    } `json:"table_contents"`

}


func main() {

    result:= []byte(`{"table_contents":[{"id":100,"description":"text100"},{"id":101,"description":"text101"},{"id":1,"description":"text1"}]}`)

    var container MyStruct1

    err := json.Unmarshal(result, &container)

    if err != nil {

        fmt.Println(" [0] Error message: " + err.Error())

        return

    }

    

    for i := range container.TableContents {

        fmt.Println(container.TableContents[i].Description)

    }

    

}

但是你如何处理下面的 JSON 响应呢?{"table_contents":[[{"id":100,"description":"text100"},{"id":101,"description":"text101"}],{"id":1,"description":"text1"}]}您可以获得此响应或上述响应,重要的是修改结构以接受两者。


在互联网的帮助下,我做了这样的事情:


package main


import (

    "fmt"

    "encoding/json"

)


type MyStruct1 struct {

    TableContents []TableContentUnion `json:"table_contents"`

}


type TableContentClass struct {

    ID          int        

    Description string

}


type TableContentUnion struct {

    TableContentClass      *TableContentClass

    TableContentClassArray []TableContentClass

}


func main() {

    result:= []byte(`{"table_contents":[[{"id":100,"description":"text100"},{"id":101,"description":"text101"}],{"id":1,"description":"text1"}]}`)

    var container MyStruct1

    err := json.Unmarshal(result, &container)

    if err != nil {

        fmt.Println(" [0] Error message: " + err.Error())

        return

    }

    

    for i := range container.TableContents {

        fmt.Println(container.TableContents[i])

    }

    

}

但它不会超过错误消息:(


[0] 错误消息:json: cannot unmarshal array into Go struct field MyStruct1.table_contents of type main.TableContentUnion*

几个小时以来一直在努力想出一个解决方案。如果有人可以帮助我会很高兴。感谢您的阅读。如果您有任何问题,请告诉我


叮当猫咪
浏览 197回答 3
3回答

一只萌萌小番薯

里面table_contents有两个类型选项(json 对象或 json 对象列表)。您可以做的是解组到一个接口,然后在使用它时对其运行类型检查:type MyStruct1 struct {    TableContents []interface{} `json:"table_contents"`}...for i := range container.TableContents {    switch container.TableContents[i].(type){    case map[string]interface{}:        fmt.Println("json object")    case []interface{}:        fmt.Println("list")    }}从那里您可以使用一些库(例如https://github.com/mitchellh/mapstructure)将未编组的结构映射到您的TableContentClass类型。在此处查看 PoC 游乐场:https: //play.golang.org/p/NhVUhQayeL_C

当年话下

自定义UnmarshalJSON函数您还可以在具有 2 种可能性的对象上创建自定义 UnmarshalJSON 函数。在你的情况下,那将是TableContentUnion.在自定义解组器中,您可以决定如何解组内容。func (s *TableContentUnion) UnmarshalJSON(b []byte) error {    // Note that we get `b` as bytes, so we can also manually check to see    // if it is an array (starts with `[`) or an object (starts with `{`)    var jsonObj interface{}    if err := json.Unmarshal(b, &jsonObj); err != nil {        return err    }    switch jsonObj.(type) {    case map[string]interface{}:        // Note: instead of using json.Unmarshal again, we could also cast the interface        // and build the values as in the example above        var tableContentClass TableContentClass        if err := json.Unmarshal(b, &tableContentClass); err != nil {            return err        }        s.TableContentClass = &tableContentClass    case []interface{}:        // Note: instead of using json.Unmarshal again, we could also cast the interface        // and build the values as in the example above        if err := json.Unmarshal(b, &s.TableContentClassArray); err != nil {            return err        }    default:        return errors.New("TableContentUnion.UnmarshalJSON: unknown content type")    }    return nil}然后其余的工作就像您之前失败的测试代码一样。这里是工作的Go Playground解组map并手动构建结构您始终可以将 json(在根目录下有一个对象)解组为map[string]interface{}. 然后,您可以在检查它们是什么类型后迭代并进一步解组它们。工作示例:func main() {    result := []byte(`{"table_contents":[[{"id":100,"description":"text100"},{"id":101,"description":"text101"}],{"id":1,"description":"text1"}]}`)    var jsonMap map[string]interface{}    err := json.Unmarshal(result, &jsonMap)    if err != nil {        fmt.Println(" [0] Error message: " + err.Error())        return    }    cts, ok := jsonMap["table_contents"].([]interface{})    if !ok {        // Note: nil or missing 'table_contents" will also lead to this path.        fmt.Println("table_contents is not a slice")        return    }    var unions []TableContentUnion    for _, content := range cts {        var union TableContentUnion        if contents, ok := content.([]interface{}); ok {            for _, content := range contents {                contCls := parseContentClass(content)                if contCls == nil {                    continue                }                union.TableContentClassArray = append(union.TableContentClassArray, *contCls)            }        } else {            contCls := parseContentClass(content)            union.TableContentClass = contCls        }        unions = append(unions, union)    }    container := MyStruct1{        TableContents: unions,    }    for i := range container.TableContents {        fmt.Println(container.TableContents[i])    }}func parseContentClass(value interface{}) *TableContentClass {    m, ok := value.(map[string]interface{})    if !ok {        return nil    }    return &TableContentClass{        ID:          int(m["id"].(float64)),        Description: m["description"].(string),    }}如果 json 有太多变化,这是最有用的。对于这样的情况,有时切换到工作方式不同的 json 包也可能有意义,例如https://github.com/tidwall/gjson,它根据路径获取值。

翻翻过去那场雪

使用json.RawMessage捕获 JSON 文档的不同部分。根据需要解组每个原始消息。func (ms *MyStruct1) UnmarshalJSON(data []byte) error {    // Declare new type with same base type as MyStruct1.    // This breaks recursion in call to json.Unmarshal below.    type x MyStruct1    v := struct {        *x        // Override TableContents field with raw message.        TableContents []json.RawMessage `json:"table_contents"`    }{        // Unmarshal all but TableContents directly to the        // receiver.        x: (*x)(ms),    }    err := json.Unmarshal(data, &v)    if err != nil {        return err    }        // Unmarahal raw elements as appropriate.    for _, tcData := range v.TableContents {        if bytes.HasPrefix(tcData, []byte{'{'}) {            var v TableContentClass            if err := json.Unmarshal(tcData, &v); err != nil {                return err            }            ms.TableContents = append(ms.TableContents, v)        } else {            var v []TableContentClass            if err := json.Unmarshal(tcData, &v); err != nil {                return err            }            ms.TableContents = append(ms.TableContents, v)        }    }    return nil}像这样使用它:var container MyStruct1err := json.Unmarshal(result, &container)if err != nil {    // handle error }这种方法不会添加任何外部依赖项。MyStruct1在或中添加或删除字段时,无需修改功能代码TableContentClass。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go