猿问

在 Go 中使用匿名成员扁平化编组的 JSON 结构

鉴于以下代码:

package main


import (

    "encoding/json"

    "fmt"

)


type User struct {

    Id   int    `json:"id"`

    Name string `json:"name"`

}


type Session struct {

    Id     int `json:"id"`

    UserId int `json:"userId"`

}


type Anything interface{}


type Hateoas struct {

    Anything

    Links map[string]string `json:"_links"`

}


func MarshalHateoas(subject interface{}) ([]byte, error) {

    h := &Hateoas{subject, make(map[string]string)}

    switch s := subject.(type) {

    case *User:

        h.Links["self"] = fmt.Sprintf("http://user/%d", s.Id)

    case *Session:

        h.Links["self"] = fmt.Sprintf("http://session/%d", s.Id)

    }

    return json.MarshalIndent(h, "", "    ")

}


func main() {

    u := &User{123, "James Dean"}

    s := &Session{456, 123}

    json, err := MarshalHateoas(u)

    if err != nil {

        panic(err)

    } else {

        fmt.Println("User JSON:")

        fmt.Println(string(json))

    }

    json, err = MarshalHateoas(s)

    if err != nil {

        panic(err)

    } else {

        fmt.Println("Session JSON:")

        fmt.Println(string(json))

    }

}

在我的情况下,我试图让渲染的 JSON 看起来正确,这意味着:


User JSON:

{

    "id": 123,

    "name": "James Dean",

    "_links": {

        "self": "http://user/123"

    }

}

Session JSON:

{

    "id": 456,

    "userId": 123,

    "_links": {

        "self": "http://session/456"

    }

}

文档中没有关于处理 JSON 中匿名成员的明确文档

匿名结构字段通常被编组,就好像它们的内部导出字段是外部结构中的字段一样,受通常的 Go 可见性规则的约束,如下一段所述。在其 JSON 标记中给出名称的匿名结构字段被视为具有该名称,而不是匿名的。

匿名结构体字段的处理在 Go 1.1 中是新的。在 Go 1.1 之前,匿名结构体字段被忽略。要强制忽略当前版本和早期版本中的匿名结构字段,请为该字段指定一个 JSON 标记“-”。

这并不清楚是否有办法使之变平,或向 Marshaller 暗示我正在尝试做的事情。

我确信可能存在,因为存在特殊情况,具有特殊含义的魔术名称可以在 XML 编组器中重命名 XML 文档的根元素。

在这种情况下,我也没有以任何方式附加到代码,我的用例是有一个函数来接受interface{}, *http.Request, http.ResponseWriter和写回 HATEOAS 文档,打开传递的类型,以推断要写回的链接进入 JSON。(因此访问请求,请求主机、端口、方案等,以及类型本身以推断 URL 和已知字段等)


明月笑刀无情
浏览 311回答 3
3回答

沧海一幻觉

我认为您尝试生成的 JSON 不是有效的 JSON 对象,因此这可能是 JsonMarshal 不与您玩游戏的原因。该对象可能无法通过 JavaScript 使用,因为它包含两个对象,除非您将这些对象包装在一个数组中。[    {        "id": 123,        "name": "James Dean",        "_links": {            "self": "http://user/123"        }    },    {        "id": 456,        "userId": 123,        "_links": {            "self": "http://session/456"        }    }]然后你就可以使用这个 JSON,例如:var user, session;user = jsonString[0]; session = jsonString[1];考虑给你的对象根名称可能是一个更好的考虑,例如:{    "user": {        "id": 123,        "name": "James Dean",        "_links": {            "self": "http://user/123"        }    },    "session": {        "id": 456,        "userId": 123,        "_links": {            "self": "http://session/456"        }    }}并消耗为,例如:var user, session;user = jsonString.user;session = jsonString.session;我希望这可以帮助你

大话西游666

利用omitempty标签和一些逻辑,以便您可以使用单一类型为不同情况生成正确的输出。诀窍是知道 JSON 编码器何时将值视为空。从 encoding/json 文档:空值是 false、0、任何 nil 指针或接口值,以及任何长度为零的数组、切片、映射或字符串。这是您的程序稍作修改以产生您想要的输出。当某些字段的值为“空”时,它会省略某些字段 - 具体而言,JSON 编码器将省略带有“0”作为值的整数并使用零长度映射。package mainimport (    "encoding/json"    "fmt")type User struct {    Id     int               `json:"id"`    Name   string            `json:"name,omitempty"`    UserId int               `json:"userId,omitempty"`    Links  map[string]string `json:"_links,omitempty"`}func Marshal(u *User) ([]byte, error) {    u.Links = make(map[string]string)    if u.UserId != 0 {        u.Links["self"] = fmt.Sprintf("http://user/%d", u.UserId)    } else if u.Id != 0 {        u.Links["self"] = fmt.Sprintf("http://session/%d", u.Id)    }    return json.MarshalIndent(u, "", "    ")}func main() {    u := &User{Id: 123, Name: "James Dean"}    s := &User{Id: 456, UserId: 123}    json, err := Marshal(u)    if err != nil {        panic(err)    } else {        fmt.Println(string(json))    }    json, err = Marshal(s)    if err != nil {        panic(err)    } else {        fmt.Println(string(json))    }}
随时随地看视频慕课网APP

相关分类

Go
我要回答