在 protobuf 文件中使用 google.protobuf.Value,并在结构中使用 go interface{



我有 protobuf 消息,看起来像这样:

// Rule : Includes an entity , an operation and a comparison value
message Rule {
string entity = 1;
string operator = 2;
google.protobuf.Value value = 3;
}

我在这里使用值,因为值可以是任何类型的。

在我的 Golang 代码中,我正在使用生成的代码:

type Rule struct {
Entity string
Operator string
Value interface{}
}

现在,我的问题是: 如何将由 google.protobuf.Value 的种类(nil、数字、字符串、布尔值、结构体、列表(表示的类型转换为接口{} 并将运行时接口{}的动态类型转换回Google.protobuf.Value类型?

我真的可以使用一些帮助,在这里找到了一个解决方案(https://go.googlesource.com/protobuf/+/4c057caa84dc5e1bc6bf0d9fe8af9180a6151e07/internal/value/convert.go(,但希望有一个更简单的方法来解决这个问题。

为了完整起见,注释:

func convertToValue(v interface{}, from reflect.Type) (*types.Value, error) {
switch from.Kind() {
case reflect.String:
if reflect.ValueOf(v).String() == "true" || reflect.ValueOf(v).String() == "false" {
boolVal, err := strconv.ParseBool(reflect.ValueOf(v).String())
if err != nil {
return nil, err
}
return &types.Value{Kind:&types.Value_BoolValue{BoolValue:boolVal}}, nil
}
return &types.Value{Kind:&types.Value_StringValue{StringValue:reflect.ValueOf(v).String()}}, nil
case reflect.Int64:
case reflect.Float64:
return &types.Value{Kind:&types.Value_NumberValue{NumberValue:reflect.ValueOf(v).Float()}}, nil
case reflect.Slice:
list := reflect.ValueOf(v)
outputList := make([]*types.Value,0)
for i:=0 ; i < list.Len(); i++ {
val, err := convertToValue(list.Index(i).Interface(), reflect.TypeOf(list.Index(i).Interface()))
if err != nil {
return nil, err
}
outputList = append(outputList, val)
}
return &types.Value{Kind:&types.Value_ListValue{ListValue:&types.ListValue{Values:outputList}}}, nil
case reflect.Struct:
valStruct := reflect.ValueOf(v)
outputMap := make(map[string]*types.Value)
numOfField := valStruct.NumField()
for i := 0; i < numOfField; i++ {
val, err := convertToValue(valStruct.Field(i).Interface(), reflect.TypeOf(valStruct.Field(i).Interface()))
if err != nil {
return nil, err
}
outputMap[valStruct.Field(i).Type().Name()] = val
}
return &types.Value{Kind:&types.Value_StructValue{StructValue:&types.Struct{Fields:outputMap}}}, nil
default:
return nil, nil
}

func convertValueToInterface(value types.Value) (interface{}, error) {
switch {
case _ , ok := value.Kind.(*types.Value_NullValue); ok:
return nil, nil
case x , ok := value.Kind.(*types.Value_StringValue); ok:
return x.StringValue, nil
case x , ok := value.Kind.(*types.Value_NumberValue); ok:
return x.NumberValue, nil
case x , ok := value.Kind.(*types.Value_BoolValue); ok:
return strconv.FormatBool(x.BoolValue), nil
case x , ok := value.Kind.(*types.Value_ListValue); ok:
if x == nil || x.ListValue == nil || x.ListValue.Values == nil {
return nil, nil
}
listValue := x.ListValue.Values
if len(listValue) == 0 {
return nil, nil
}
val, err := convertValueToInterface(*listValue[0])
if err != nil {
return nil, err
}
typ := reflect.TypeOf(val)
outputList := reflect.MakeSlice(reflect.SliceOf(typ),0,0)
for _, value := range listValue {
if value != nil {
val, err := convertValueToInterface(*value)
if err != nil {
return nil, err
}
outputList = reflect.Append(outputList, reflect.ValueOf(val))
}
}
return outputList.Interface(), nil
case x , ok := value.Kind.(*types.Value_StructValue); ok:
if x == nil || x.StructValue == nil || x.StructValue.Fields == nil {
return nil, nil
}
mapValue := x.StructValue.Fields
var keyTyp reflect.Type
var typ reflect.Type
for key,value := range mapValue {
if value != nil {
val, err := convertValueToInterface(*value)
if err != nil {
return nil, err
}
keyTyp = reflect.TypeOf(key)
typ = reflect.TypeOf(val)
break
} else {
return nil, nil
}
}
outputMap := reflect.MakeMap(reflect.MapOf(keyTyp, typ))
for key,value := range mapValue {
if value != nil {
val, err := convertValueToInterface(*value)
if err != nil {
return nil, err
}
outputMap.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(val))
}
}
return outputMap.Interface(), nil
default:
return nil, nil
}

最新更新