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

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

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

Go
叮当猫咪 2022-06-27 15:40:53
对于以下 JSON 响应{"table_contents":[{"id":100,"description":"text100"},{"id":101,"description":"text101"},{"id":1,"description":"text1"}]}您所要做的就是生成以下代码以正确执行它并能够从结构中读取字段,例如:package mainimport (    "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 mainimport (    "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*几个小时以来一直在努力想出一个解决方案。如果有人可以帮助我会很高兴。感谢您的阅读。如果您有任何问题,请告诉我
查看完整描述

3 回答

?
一只萌萌小番薯

TA贡献1795条经验 获得超7个赞

里面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


查看完整回答
反对 回复 2022-06-27
?
当年话下

TA贡献1890条经验 获得超9个赞

自定义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,它根据路径获取值。


查看完整回答
反对 回复 2022-06-27
?
翻翻过去那场雪

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

使用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 MyStruct1

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

if err != nil {

    // handle error 

}


这种方法不会添加任何外部依赖项。MyStruct1在或中添加或删除字段时,无需修改功能代码TableContentClass。


查看完整回答
反对 回复 2022-06-27
  • 3 回答
  • 0 关注
  • 195 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号