从结构中删除字段或将其隐藏在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
采取三个成分:
-
reflect
包循环结构的所有字段。 -
一个
if
语句来拾取你想要Marshal
的领域,并且 -
encoding/json
软件包,以Marshal
你喜欢的领域。
制备:
-
把它们混合在一个很好的比例。 使用
reflect.TypeOf(your_struct).Field(i).Name()
来获取your_struct
的第i
个字段的your_struct
。 -
使用
reflect.ValueOf(your_struct).Field(i)
来获得your_struct
第i
个字段的typesValue
表示。 -
使用
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{}
。
我刚刚发布了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) }