123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- package jsonpb
- import (
- "net/url"
- "reflect"
- "strings"
- "kpt-tmr-group/pkg/xerr"
- "kpt-tmr-group/pkg/xreflect"
- "github.com/golang/protobuf/proto"
- )
- var emptyField = reflect.StructField{}
- func UnmarshalQuery(values url.Values, pb proto.Message) error {
- target := reflect.ValueOf(pb).Elem()
- targetType := target.Type()
- if targetType.Kind() != reflect.Struct {
- return xerr.New("target should be struct")
- }
- sprops := proto.GetProperties(targetType)
- for i := 0; i < target.NumField(); i++ {
- ft := target.Type().Field(i)
- if strings.HasPrefix(ft.Name, "XXX_") {
- continue
- }
- getField := func(prop *proto.Properties) ([]string, bool) {
- // Be liberal in what names we accept; both orig_name and camelName are okay.
- camel, orig := prop.JSONName, prop.OrigName
- keys := []string{camel, orig}
- // handle condition xxx[]=1&xxx[]=2
- if prop.Repeated {
- keys = append(keys, camel+"[]", orig+"[]")
- }
- for _, key := range keys {
- v, ok := values[key]
- if ok {
- return v, true
- }
- }
- return nil, false
- }
- valueForField, ok := getField(sprops.Prop[i])
- if !ok {
- continue
- }
- err := setProtoByQueryValue(target.Field(i), valueForField, sprops.Prop[i])
- if err != nil {
- return err
- }
- }
- return nil
- }
- func setProtoByQueryValue(target reflect.Value, queryValue []string, prop *proto.Properties) (err error) {
- if len(queryValue) == 0 {
- if prop.HasDefault {
- queryValue = []string{prop.Default}
- } else {
- return nil
- }
- }
- if target.Kind() == reflect.Slice {
- slice := reflect.MakeSlice(target.Type(), len(queryValue), len(queryValue))
- for i, s := range queryValue {
- err := setProtoByQueryValue(slice.Index(i), []string{s}, prop)
- if err != nil {
- return err
- }
- }
- target.Set(slice)
- return nil
- }
- // Handle enums, which have an underlying type of int32,
- // and may appear as strings.
- // The case of an enum appearing as a number is handled
- // at the bottom of this function.
- if prop != nil && prop.Enum != "" {
- vmap := proto.EnumValueMap(prop.Enum)
- if len(queryValue) == 1 {
- // Don't need to do unquoting; valid enum names
- // are from a limited character set.
- s := queryValue[0]
- n, ok := vmap[s]
- if !ok {
- return xerr.Errorf("unknown value %q for enum %s", s, prop.Enum)
- }
- return setProtoEnum(n, target, prop)
- }
- }
- return xreflect.SetString(target, queryValue[0])
- }
- func setProtoEnum(val int32, target reflect.Value, prop *proto.Properties) error {
- if target.Kind() == reflect.Ptr { // proto2
- target.Set(reflect.New(target.Type().Elem()))
- target = target.Elem()
- }
- if target.Kind() != reflect.Int32 {
- return xerr.Errorf("invalid target %q for enum %s", target.Kind(), prop.Enum)
- }
- target.SetInt(int64(val))
- return nil
- }
|