gpt4 book ai didi

json - 在 Go 中使用匿名成员展平编码的 JSON 结构

转载 作者:IT王子 更新时间:2023-10-29 01:15:27 25 4
gpt4 key购买 nike

给定以下代码:( reproduced here at play.golang.org .)

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"
}
}

不幸的是,Go 将匿名成员视为真正命名的事物,因此它采用定义的类型(Anything)并这样命名 JSON:

User JSON:
{
"Anything": {
"id": 123,
"name": "James Dean"
},
"_links": {
"self": "http://user/123"
}
}
Session JSON:
{
"Anything": {
"id": 456,
"userId": 123
},
"_links": {
"self": "http://session/456"
}
}

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

Anonymous struct fields are usually marshaled as if their inner exported fields were fields in the outer struct, subject to the usual Go visibility rules amended as described in the next paragraph. An anonymous struct field with a name given in its JSON tag is treated as having that name, rather than being anonymous.

Handling of anonymous struct fields is new in Go 1.1. Prior to Go 1.1, anonymous struct fields were ignored. To force ignoring of an anonymous struct field in both current and earlier versions, give the field a JSON tag of "-".

这并不清楚是否有办法展平,或向编码器提示我正在尝试做什么。

我敢肯定,在特殊情况下,可能存在具有特殊含义的魔术名称,可以在 XML 编码器中重命名 XML 文档的根元素。

在这种情况下,我也不以任何方式依附于代码,我的用例是有一个函数接受 interface{}, *http.Request, http.ResponseWriter并沿着线路写回 HATEOAS 文档,切换传递的类型,以推断将哪些链接写回 JSON。 (因此访问请求,请求主机、端口、方案等,以及类型本身以推断 URL 和已知字段等)

最佳答案

工作 Playground 链接:http://play.golang.org/p/_r-bQIw347

它的要点是这样的;通过使用 reflect 包,我们遍历我们希望序列化的结构的字段并将它们映射到 map[string]interface{} 我们现在可以保留原始结构的平面结构而不引入新的字段。

注意买者,可能应该针对此代码中所做的一些假设进行多次检查。例如,它假设 MarshalHateoas 总是接收指向值的指针。

package main

import (
"encoding/json"
"fmt"
"reflect"
)

type User struct {
Id int `json:"id"`
Name string `json:"name"`
}

type Session struct {
Id int `json:"id"`
UserId int `json:"userId"`
}

func MarshalHateoas(subject interface{}) ([]byte, error) {
links := make(map[string]string)
out := make(map[string]interface{})
subjectValue := reflect.Indirect(reflect.ValueOf(subject))
subjectType := subjectValue.Type()
for i := 0; i < subjectType.NumField(); i++ {
field := subjectType.Field(i)
name := subjectType.Field(i).Name
out[field.Tag.Get("json")] = subjectValue.FieldByName(name).Interface()
}
switch s := subject.(type) {
case *User:
links["self"] = fmt.Sprintf("http://user/%d", s.Id)
case *Session:
links["self"] = fmt.Sprintf("http://session/%d", s.Id)
}
out["_links"] = links
return json.MarshalIndent(out, "", " ")
}
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 - 在 Go 中使用匿名成员展平编码的 JSON 结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20362147/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com