从结构中删除字段或将其隐藏在JSON响应中

我在Go中创build了一个API,在被调用的时候,执行一个查询,创build一个结构的实例,然后在返回给调用者之前将结构编码为JSON。 我现在想允许调用者能够通过传递“fields”GET参数来select他们想返回的特定字段。

这意味着取决于字段的值,我的结构会改变。 有没有办法从结构中删除字段? 或者至lessdynamic地将它们隐藏在JSON响应中? (注意:有时我有空值,所以JSON omitEmpty标签将不能在这里工作)如果这些都不可能,是否有更好的方法来处理这个问题的build议? 提前致谢。

下面是我使用的较小版本的结构:

type SearchResult struct { Date string `json:"date"` IdCompany int `json:"idCompany"` Company string `json:"company"` IdIndustry interface{} `json:"idIndustry"` Industry string `json:"industry"` IdContinent interface{} `json:"idContinent"` Continent string `json:"continent"` IdCountry interface{} `json:"idCountry"` Country string `json:"country"` IdState interface{} `json:"idState"` State string `json:"state"` IdCity interface{} `json:"idCity"` City string `json:"city"` } //SearchResult type SearchResults struct { NumberResults int `json:"numberResults"` Results []SearchResult `json:"results"` } //type SearchResults 

然后我编码并输出响应如下所示:

 err := json.NewEncoder(c.ResponseWriter).Encode(&msg) 

编辑:我注意到一些downvotes,再看看这个问答。 大多数人似乎都错过了OP要求根据调用者提供的字段列表来dynamicselect字段。 你不能用静态定义的json结构标签来做到这一点。

如果你想要总是跳过一个字段json-encode,那么当然使用json:"-"来忽略该字段(还要注意,如果你的字段是未导出的,这是不需要的 – 这些字段总是被json忽略编码器)。 但这不是OP的问题。

引用json:"-"的评论json:"-"答案:

这个[ json:"-"答案)是大多数人在search结果中想要的答案,但这不是问题的答案。


在这种情况下,我会使用map [string] interface {}而不是结构。 您可以通过调用地图上内置的delete删除字段来轻松删除字段。

也就是说,如果你不能只查询请求的字段。

使用`json:“ – ”`

 // Field is ignored by this package. Field int `json:"-"` // Field appears in JSON as key "myName". Field int `json:"myName"` // Field appears in JSON as key "myName" and // the field is omitted from the object if its value is empty, // as defined above. Field int `json:"myName,omitempty"` // Field appears in JSON as key "Field" (the default), but // the field is skipped if empty. // Note the leading comma. Field int `json:",omitempty"` 

doc: http : //golang.org/pkg/encoding/json/#Marshal

另一种方法是使用,omitempty标记来,omitempty 指针结构。 如果指针是 ,字段将不会被统一。

这种方法不需要额外的reflection或低效的地图使用。

与使用此方法的jorelli相同的示例: http ://play.golang.org/p/JJNa0m2_nw

您可以使用reflect包来select所需的字段,方法是reflection字段标记并selectjson标记值。 在你的SearchResultstypes上定义一个方法,select你想要的字段并将它们作为map[string]interface{} ,然后编组而不是SearchResults结构本身。 下面是一个如何定义该方法的例子:

 func fieldSet(fields ...string) map[string]bool { set := make(map[string]bool, len(fields)) for _, s := range fields { set[s] = true } return set } func (s *SearchResult) SelectFields(fields ...string) map[string]interface{} { fs := fieldSet(fields...) rt, rv := reflect.TypeOf(*s), reflect.ValueOf(*s) out := make(map[string]interface{}, rt.NumField()) for i := 0; i < rt.NumField(); i++ { field := rt.Field(i) jsonKey := field.Tag.Get("json") if fs[jsonKey] { out[jsonKey] = rv.Field(i).Interface() } } return out } 

这里有一个可运行的解决scheme,显示你将如何调用这个方法并编组你的select: http : //play.golang.org/p/1K9xjQRnO8

采取三个成分:

  1. reflect包循环结构的所有字段。

  2. 一个if语句来拾取你想要Marshal的领域,并且

  3. encoding/json软件包,以Marshal你喜欢的领域。

制备:

  1. 把它们混合在一个很好的比例。 使用reflect.TypeOf(your_struct).Field(i).Name()来获取your_struct的第i个字段的your_struct

  2. 使用reflect.ValueOf(your_struct).Field(i)来获得your_structi个字段的typesValue表示。

  3. 使用fieldValue.Interface{}检索valueValuetypes的实际值(上传到键入interface {})

如果你幸运地在这个过程中不要烧毁任何晶体pipe或断路器,你应该得到这样的东西:

 func MarshalOnlyFields(structa interface{}, includeFields map[string]bool) (jsona []byte, status error) { value := reflect.ValueOf(structa) typa := reflect.TypeOf(structa) size := value.NumField() jsona = append(jsona, '{') for i := 0; i < size; i++ { structValue := value.Field(i) var fieldName string = typa.Field(i).Name if marshalledField, marshalStatus := json.Marshal((structValue).Interface()); marshalStatus != nil { return []byte{}, marshalStatus } else { if includeFields[fieldName] { jsona = append(jsona, '"') jsona = append(jsona, []byte(fieldName)...) jsona = append(jsona, '"') jsona = append(jsona, ':') jsona = append(jsona, (marshalledField)...) if i+1 != len(includeFields) { jsona = append(jsona, ',') } } } } jsona = append(jsona, '}') return } 

服务:

例如,使用任意的结构和map[string]bool的字段,你想包括的字段

 type magic struct { Magic1 int Magic2 string Magic3 [2]int } func main() { var magic = magic{0, "tusia", [2]int{0, 1}} if json, status := MarshalOnlyFields(magic, map[string]bool{"Magic1": true}); status != nil { println("error") } else { fmt.Println(string(json)) } } 

好胃口!

您可以使用标记属性“omitifempty”或使可选字段指针,离开那些你想跳过未初始化。

现在这个问题已经有点老了,但是我刚才也遇到了同样的问题,因为我发现没有简单的方法来做到这一点,所以我build立了一个库来实现这个目的。 它允许从一个静态结构中轻松地生成一个map[string]interface{}

https://github.com/tuvistavie/structomap

我刚刚发布了sheriff ,它根据结构字段上注释的标签将结构转换为地图。 然后你可以编组(JSON或其他)生成的地图。 它可能不允许你只序列化调用者要求的字段集,但我想象一下使用一组组将允许​​你覆盖大多数情况。 直接使用组而不是字段将最有可能增加caching能力。

例:

 package main import ( "encoding/json" "fmt" "log" "github.com/hashicorp/go-version" "github.com/liip/sheriff" ) type User struct { Username string `json:"username" groups:"api"` Email string `json:"email" groups:"personal"` Name string `json:"name" groups:"api"` Roles []string `json:"roles" groups:"api" since:"2"` } func main() { user := User{ Username: "alice", Email: "alice@example.org", Name: "Alice", Roles: []string{"user", "admin"}, } v2, err := version.NewVersion("2.0.0") if err != nil { log.Panic(err) } o := &sheriff.Options{ Groups: []string{"api"}, ApiVersion: v2, } data, err := sheriff.Marshal(o, user) if err != nil { log.Panic(err) } output, err := json.MarshalIndent(data, "", " ") if err != nil { log.Panic(err) } fmt.Printf("%s", output) }