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

如何使用ScalaPB在Scala中解析任意Protobuf消息并转为JSON?

解决ScalaPB解析未知Protobuf并转JSON的类型错误问题

问题原因

你直接将原生com.google.protobuf.Any传入ScalaPB的Printer.print()方法,而该方法仅接受ScalaPB生成的scalapb.GeneratedMessage类型,因此触发类型不匹配错误。原生Any是一个包装容器,需要先解析出内部的具体消息实例才能被ScalaPB的JSON工具处理。

解决方案

场景1:已知可能的消息类型(推荐)

如果能提前预判Any中可能封装的消息类型,可通过TypeRegistry注册这些类型,再使用Printer的printAny()方法处理:

import com.google.protobuf.Any
import scalapb.json4s.Printer
import scalapb.json4s.TypeRegistry

// 注册所有可能出现的消息类型(替换为你的实际消息伴生对象)
val typeRegistry = TypeRegistry(
  OrderMessage,
  UserMessage
  // 其他消息类型...
)

// 解析原生Any
val protoAny: Any = Any.parseFrom(request.contentRaw)

// 创建带类型注册表的Printer
val printer = Printer(preservingProtoFieldNames = true)
  .withTypeRegistry(typeRegistry)

// 转换为JSON
val json = printer.printAny(protoAny)

场景2:完全未知消息类型

如果无法提前知晓消息类型,需通过消息描述符(Descriptor)将Any解析为DynamicMessage,再进行JSON转换:

import com.google.protobuf.{Any, DynamicMessage, Descriptors}
import scalapb.json4s.Printer

// 解析原生Any
val protoAny: Any = Any.parseFrom(request.contentRaw)

// 从Any的typeUrl获取消息描述符(需确保能加载到对应.proto的描述符)
// 示例:通过DescriptorRegistry加载目标消息描述符
val descriptorRegistry = Descriptors.FileDescriptorRegistry.getEmptyRegistry()
// 假设已将目标消息的FileDescriptor加载到registry中
val descriptor: Descriptors.Descriptor = descriptorRegistry.findMessageTypeByName(
  protoAny.getTypeUrl.split("/").last
)

// 将Any解析为DynamicMessage
val dynamicMsg = DynamicMessage.parseFrom(descriptor, protoAny.getValue)

// 转换为JSON
val printer = Printer(preservingProtoFieldNames = true)
val json = printer.print(dynamicMsg)

关键说明

  • printAny()是ScalaPB专门为处理com.google.protobuf.Any提供的方法,需配合TypeRegistry使用,否则会因无法识别消息类型抛出异常。
  • 若使用DynamicMessage,需确保能获取到对应消息的描述符,通常可通过预加载所有.proto文件的FileDescriptor到DescriptorRegistry实现。

内容的提问来源于stack exchange,提问作者Sim

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 22:40:57