You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 17:27:35