在Go中使用匿名成员展平编组的JSON结构 [英] Flattening marshalled JSON structs with anonymous members in Go

查看:123
本文介绍了在Go中使用匿名成员展平编组的JSON结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给出以下代码:( 在此处复制在play.golang.org .)

Given the following code: (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看起来正确,这意味着:

I'm attempting to have the rendered JSON look correct in my case that means something like:

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:

Unfortunately Go is treating the anonymous member as a real named thing, so it's taking the defined type (Anything) and naming the JSON thusly:

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

文档:

通常将匿名结构字段编组为好像其内部导出字段是外部结构中的字段一样,但要遵守下一段所述的常规Go可见性规则的修订.在其JSON标记中具有名称的匿名struct字段被视为具有该名称,而不是匿名.

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.

处理匿名结构字段是Go 1.1中的新增功能.在Go 1.1之前,匿名结构字段被忽略.要强制忽略当前版本和早期版本中的匿名struct字段,请为该字段提供JSON标签-".

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

这并不清楚是否有一种方法可以解决问题,或向Marshaller提示我要做什么.

This doesn't make clear if there's a way to flatten out, or hint to the Marshaller what I am trying to do.

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

I'm certain that there might be, as there is a special case, magic name that has a special meaning to rename the root element of an XML document in the XML marshaller.

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

In this case, I'm also not attached to the code in any way, my use-case is to have a function that accepts interface{}, *http.Request, http.ResponseWriter and write back HATEOAS documents down the wire, switching on the type passed, to infer which links to write back into the JSON. (thus access to the request, for request host, port, scheme, etc, as well as to the type itself to infer the URL and known fields, etc)

推荐答案

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

要点是这个;通过使用反射包,我们遍历要序列化的结构的字段并将其映射到map[string]interface{},我们现在可以保留原始结构的平面结构,而无需引入新的字段.

The gist of it is this; by using the reflect package we loop over the fields of the struct we wish to serialize and map them to a map[string]interface{} we can now retain the flat structure of the original struct without introducing new fields.

随心所欲的人,可能应该对此代码中的某些假设进行多次检查.例如,假设MarshalHateoas始终接收指向值的指针.

Caveat emptor, there should probably be several checks against some of the assumptions made in this code. For instance it assumes that MarshalHateoas always receives pointers to values.

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

这篇关于在Go中使用匿名成员展平编组的JSON结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆