如何获取Protocol Buffer嵌套消息的父消息?
Protobuf嵌套消息获取父消息的实现方案
现有嵌套消息结构:类型为B的消息b嵌套在类型为A的消息a中,需要实现类似b.getParent()的方式来获取父消息a。
定义的Protobuf消息
message B { optional string name = 1; repeated B b = 2; } message A { optional string name = 1; repeated B b = 1; }
a实例的文本序列化示例
name: "a" b { name: "foo" b { name: "fred" b { name: "flintstone" }}} b { name: "bar" } b { name: "baz" }
问题场景
使用访问者模式遍历集合a时,需要重构由所有祖先名称组成的完整名称。
实现方案
Protobuf原生并未提供父消息的引用机制,需要通过以下方式手动实现:
遍历过程传递上下文
这是最直接的方案,无需修改Protobuf定义。在访问者遍历逻辑中,处理子节点时同步传递父消息实例和当前祖先名称链。示例Kotlin代码如下:// 遍历A节点,初始化父名称链和父消息 fun traverseA(a: A) { val initialNames = if (a.name.isNotEmpty()) listOf(a.name) else emptyList() traverseB(a.bList, initialNames, parentMessage = a) } // 递归遍历B节点,传递父上下文 fun traverseB(bList: List<B>, parentNames: List<String>, parentMessage: Any) { bList.forEach { b -> // 构建当前节点的完整名称链 val fullName = parentNames + b.name println("当前节点完整名称: ${fullName.joinToString(".")}") // 直接使用parentMessage获取父消息实例 // 处理当前B节点的业务逻辑... // 递归遍历子B节点,更新上下文 traverseB(b.bList, fullName, parentMessage = b) } }手动维护父引用映射
如果需要在任意时刻获取父消息,可以在构建或遍历消息树时,用WeakHashMap(避免内存泄漏)存储每个B实例对应的父消息(A或B)。示例:val parentMap = WeakHashMap<Any, Any>() // 构建消息树时绑定父引用 fun buildAndBindParent(a: A) { a.bList.forEach { b -> parentMap[b] = a bindChildBParent(b, b) } } fun bindChildBParent(parentB: B, childB: B) { childB.bList.forEach { subB -> parentMap[subB] = parentB bindChildBParent(childB, subB) } } // 封装成Kotlin扩展 fun Any.getParent(): Any? = parentMap[this]这种方式适合频繁获取父消息的场景,但需注意管理映射生命周期,防止内存泄漏。
修改Protobuf定义(可选)
若允许修改.proto文件,可以给B添加一个不参与序列化的父消息字段,比如:import "google/protobuf/descriptor.proto"; message B { optional string name = 1; repeated B b = 2; // 标记该字段不参与序列化 optional google.protobuf.Message parent = 3 [deprecated = true, json_name = ""]; }不过这种方式会增加消息结构复杂度,且需确保序列化时忽略该字段,一般不推荐。
内容的提问来源于stack exchange,提问作者phreed
相关产品推荐
相关产品推荐

