我在Go中创建了一个API,在被调用时,执行查询,创建一个结构实例,然后在发送回调用者之前将该结构编码为JSON。现在,我想允许调用者通过传入“fields”GET参数来选择他们想要返回的特定字段。

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

下面是我正在使用的结构体的一个较小版本:

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)

当前回答

以三种成分为例:

用于遍历结构的所有字段的反射包。 一个if语句,用于拾取想要编组的字段,以及 encoding/json包来封送您喜欢的字段。

准备:

把它们按适当的比例混合。使用reflect.TypeOf(your_struct). field (i). name()来获取your_struct的第i个字段的名称。 使用reflect.ValueOf(your_struct). field (i)来获取your_struct的第i个字段的类型值表示。 使用fieldValue. interface()检索类型为value的fieldValue的实际值(转换为类型interface{})(注意括号的使用- interface()方法生成interface{}

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

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

}

你好!

其他回答

另一种方法是使用带有omitempty标记的指针结构体。如果指针为nil,则字段不会被marshalling。

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

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

以三种成分为例:

用于遍历结构的所有字段的反射包。 一个if语句,用于拾取想要编组的字段,以及 encoding/json包来封送您喜欢的字段。

准备:

把它们按适当的比例混合。使用reflect.TypeOf(your_struct). field (i). name()来获取your_struct的第i个字段的名称。 使用reflect.ValueOf(your_struct). field (i)来获取your_struct的第i个字段的类型值表示。 使用fieldValue. interface()检索类型为value的fieldValue的实际值(转换为类型interface{})(注意括号的使用- interface()方法生成interface{}

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

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

}

你好!

下面是我如何定义我的结构。

type User struct {
    Username string  `json:"username" bson:"username"`
    Email    string  `json:"email" bson:"email"`
    Password *string `json:"password,omitempty" bson:"password"`
    FullName string  `json:"fullname" bson:"fullname"`
}

在函数集user里面。Password = nil表示不被封送。

使用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

您可以使用反射包通过反射字段标记和选择json标记值来选择您想要的字段。在SearchResults类型上定义一个方法,该方法选择您想要的字段,并将它们作为映射[string]接口{}返回,然后封送该方法而不是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
}

这里有一个可运行的解决方案,展示了如何调用这个方法并编组您的选择:http://play.golang.org/p/1K9xjQRnO8