Golang:使用fmutils按FieldMask过滤Protobuf Struct字段失效问题
问题原因与解决方案
fmutils库默认基于Go原生结构体的反射机制工作,但structpb.Struct的存储逻辑特殊——它的所有JSON字段都存在Fields这个map[string]*structpb.Value成员里,而非常规的结构体公开字段。fmutils无法自动识别这种嵌套的map结构,所以执行Filter后会误把整个Struct清空。
你需要针对structpb.Struct的特性做额外处理,下面提供两种可行方案:
方案一:直接操作structpb.Struct的Fields map(推荐)
既然明确structpb.Struct的存储逻辑,我们可以直接操作Fields map实现字段过滤,同时支持嵌套路径处理:
package main import ( "fmt" structpb "github.com/golang/protobuf/ptypes/struct" "google.golang.org/protobuf/encoding/protojson" "strings" ) func filterStructPb(s *structpb.Struct, paths []string) { // 收集所有需要保留的键(含嵌套路径的父键) keepKeys := make(map[string]bool) for _, path := range paths { parts := strings.Split(path, ".") currentKey := parts[0] keepKeys[currentKey] = true // 处理嵌套字段,递归保留父结构中的目标子字段 currentValue, exists := s.Fields[currentKey] if exists && currentValue.GetStructValue() != nil && len(parts) > 1 { filterStructPb(currentValue.GetStructValue(), []string{strings.Join(parts[1:], ".")}) } } // 删除不需要保留的键 for key := range s.Fields { if !keepKeys[key] { delete(s.Fields, key) } } } func main() { jsonString := `{ "field_a": true, "field_b": { "sub_field_b": 99 } }` myPbStruct := &structpb.Struct{} protojson.Unmarshal([]byte(jsonString), myPbStruct) filterStructPb(myPbStruct, []string{"field_a"}) result, _ := protojson.Marshal(myPbStruct) fmt.Println(string(result)) // 输出 {"field_a":true} }
方案二:转成Go原生map后用fmutils处理
如果必须依赖fmutils的复杂路径规则,可以先把structpb.Struct转成map[string]interface{},过滤后再转回structpb.Struct:
package main import ( "fmt" structpb "github.com/golang/protobuf/ptypes/struct" "github.com/mennanov/fmutils" "google.golang.org/protobuf/encoding/protojson" ) func main() { jsonString := `{ "field_a": true, "field_b": { "sub_field_b": 99 } }` myPbStruct := &structpb.Struct{} protojson.Unmarshal([]byte(jsonString), myPbStruct) // 转成Go原生map var data map[string]interface{} jsonBytes, _ := protojson.Marshal(myPbStruct) protojson.Unmarshal(jsonBytes, &data) // 用fmutils过滤字段 fmutils.Filter(data, []string{"field_a"}) // 转回structpb.Struct filteredPbStruct := &structpb.Struct{} protojson.Unmarshal(jsonBytes, filteredPbStruct) result, _ := protojson.Marshal(filteredPbStruct) fmt.Println(string(result)) // 输出 {"field_a":true} }
注意:方案二需要额外的序列化/反序列化步骤,性能略低于方案一,适合路径规则复杂、不想自行实现嵌套逻辑的场景。
内容的提问来源于stack exchange,提问作者hydradon
相关产品推荐
相关产品推荐

