从 Java 到 Golang:解组多态 JSON

新手 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 而言,这工作得很好。缺点是模型的使用者需要在有效负载上进行类型断言才能与子类型交互才能完成任何实际工作。理想情况下,是否有一种解决方案可以允许传递“事物”抽象级别,并且还可以在需要时与子类型进行交互?根据阅读,接口可以用于这种场景,但我正在努力了解该模型将如何利用它们。想法?


四季花海
浏览 121回答 1
1回答

倚天杖

我认为使 Thing 成为一个接口并实现 UnmarshalJSON 这几乎可以满足您的需求(如果用户需要接口未提供的功能,但这是不可避免的,他们仍然必须使用类型断言/开关)。这看起来像下面这样:package mainimport (    "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指出我错过了一个测试用例后进行了更新。代码现已测试且工作正常。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go