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

从 Java 到 Golang:解组多态 JSON

从 Java 到 Golang:解组多态 JSON

Go
四季花海 2023-08-07 15:03:49
新手 golang 程序员在这里。我正在用 go 重写 java 应用程序。java 应用程序使用一个对象模型,该模型利用 Jackson 的多态类型功能来处理与 JSON 的编组/解组。假设我无法更改 JSON 对象的形状。鉴于 go 提供的多态性是 interface{},因此就多态性而言,提出一个提供相同使用模式的“对象模型”一直是一个挑战。我第一次尝试解决这个问题看起来像这样:type Thing struct {    ID   string `json:"id"`    Type string `json:"@type"`}type SpecificThing struct {    Thing    SpecificField string `json:"specificField"`}type AnotherSpecificThing struct {    Thing    AnotherSpecificField string `json:"anotherSpecificField"`}但这需要将具体的子类型实例传递给 unmarshal 方法。我试图通过创建“Union Structs”作为编组和解组的工具来解决这个问题:type Thing struct {    ID      string      `json:"id"`    Type    string      `json:"@type"`    Payload interface{} `json:"-"`}type SpecificThing struct {    SpecificField string `json:"specificField"`}type AnotherSpecificThing struct {    AnotherSpecificField string `json:"anotherSpecificField"`}type superThing struct {    ID   string `json:"id"`    Type string `json:"@type"`    *SpecificThing    *AnotherSpecificThing}func (t *Thing) UnmarshalJSON(b []byte) error {    //error checking omitted for brevity    var st superThing    _ = json.Unmarshal(b, &st)    t.ID = st.ID    t.Type = st.Type    switch t.Type {    case "specificThing":        t.Payload = st.SpecificThing    case "anotherSpecificThing":        t.Payload = st.AnotherSpecificThing    }    return nil}func TestUnmarshal(t *testing.T) {    data := []byte(`    {        "id":"some id",        "@type":"specificThing",        "specificField": "some specific field value"    }       `)    var th Thing    _ = json.Unmarshal(data, &th)}就能够编组和解组这个动态 JSON 而言,这工作得很好。缺点是模型的使用者需要在有效负载上进行类型断言才能与子类型交互才能完成任何实际工作。理想情况下,是否有一种解决方案可以允许传递“事物”抽象级别,并且还可以在需要时与子类型进行交互?根据阅读,接口可以用于这种场景,但我正在努力了解该模型将如何利用它们。想法?
查看完整描述

1 回答

?
倚天杖

TA贡献1828条经验 获得超3个赞

我认为使 Thing 成为一个接口并实现 UnmarshalJSON 这几乎可以满足您的需求(如果用户需要接口未提供的功能,但这是不可避免的,他们仍然必须使用类型断言/开关)。这看起来像下面这样:


package main


import (

    "encoding/json"

    "fmt"

)


func main() {

    data := []byte(`

    {

        "id":"some id",

        "@type":"specificThing",

        "specificField": "some specific field value"

    }   

    `)


    var th ThingHolder 

    err := json.Unmarshal(data, &th)

    if err != nil {

        panic(err)

    }

    mySpecThing := th.T.(*SpecificThing )

    fmt.Printf("%v", mySpecThing)

}


type Thing interface {

    ID() string

}


type ThingHolder struct {

    T Thing

}


type SpecificThing struct {

    Id            string `json:"id"`

    Type          string `json:"@type"`

    SpecificField string `json:"specificField"`

}


func (s *SpecificThing) ID() string {

    return s.Id

}


func (t *ThingHolder) UnmarshalJSON(b []byte) error {

    var objMap map[string]*json.RawMessage

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

    if err != nil {

        return err

    }


    // Now lets see what 'things' the JSON contains

    // by looking at JSON keys

    jsonType, ok := objMap["@type"]

    if !ok {

        return fmt.Errorf("No Type")

    }

    var goType string

    err = json.Unmarshal(*jsonType, &goType)

    if err != nil {

    return fmt.Errorf("error getting type: %s", err)

    }   


    switch goType {

    case "specificThing":

    var st SpecificThing

        err = json.Unmarshal(b, &st)

        if err != nil {

            return err

        }

        t.T = &st

    default:

    return fmt.Errorf("Unknown type %s", goType )

    }


    return nil

}


在OP指出我错过了一个测试用例后进行了更新。代码现已测试且工作正常。


查看完整回答
反对 回复 2023-08-07
  • 1 回答
  • 0 关注
  • 77 浏览
慕课专栏
更多

添加回答

举报

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