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

如何在Scala中使用Json4s将二进制Protobuf文件转换为JSON文件?

嘿,作为Scala新手,你这个需求完全能搞定!不用搭ScalaPB的完整工具链,只用Json4s加上Protobuf的基础Java库就可以把二进制Protobuf转成JSON。下面一步步给你讲清楚:

所需依赖库

首先在你的build.sbt里添加这些依赖(版本可以根据你的项目情况调整):

libraryDependencies ++= Seq(
  "com.google.protobuf" % "protobuf-java" % "3.24.4", // 请匹配你proto文件对应的版本
  "org.json4s" %% "json4s-core" % "4.0.6",
  "org.json4s" %% "json4s-jackson" % "4.0.6"
)

另外你已经有的Protobuf生成类文件直接放在项目源码路径里就行,不需要额外配置。

具体操作步骤

1. 解析二进制Blob到Protobuf类实例

先把Blob格式的二进制数据(本质是字节数组)反序列化成你生成的Protobuf类对象。假设你的Protobuf生成类是com.example.MyBizMessage,代码示例如下:

import com.example.MyBizMessage // 替换成你自己的Protobuf生成类
import java.nio.file.{Files, Paths}

// 从文件读取Blob二进制数据(如果你的Blob来自其他渠道,替换成对应的获取方式)
val blobBytes: Array[Byte] = Files.readAllBytes(Paths.get("your-protobuf-data.bin"))
// 反序列化成Protobuf对象
val protoObj = MyBizMessage.parseFrom(blobBytes)

2. 编写Json4s自定义序列化器

Json4s默认不认识Protobuf的特殊类型(比如ByteString、枚举类型、嵌套消息、重复字段等),所以需要写一个自定义序列化器来适配这些类型:

import org.json4s._
import org.json4s.jackson.Serialization
import com.google.protobuf.{ByteString, Message, Descriptors}
import scala.jdk.CollectionConverters._

class ProtobufJsonSerializer extends CustomSerializer[Message](format => (
  {
    // 我们只需要序列化到JSON,反序列化逻辑暂时留空
    case _ => null
  },
  {
    case msg: Message =>
      JObject(
        msg.getAllFields.map { case (field, value) =>
          val fieldName = field.getName
          val jsonValue = handleProtobufField(field, value)(format)
          fieldName -> jsonValue
        }.toList
      )
  }
))

// 处理单个Protobuf字段的序列化逻辑
private def handleProtobufField(
  field: Descriptors.FieldDescriptor,
  value: Any
)(implicit format: Formats): JValue = {
  import Descriptors.FieldDescriptor.JavaType

  if (field.isRepeated) {
    val values = value.asInstanceOf[java.util.List[_]].asScala
    JArray(values.map(item => handleSingleField(field.getJavaType, item)).toList)
  } else {
    handleSingleField(field.getJavaType, value)
  }
}

// 处理单个非重复字段
private def handleSingleField(javaType: JavaType, value: Any)(implicit format: Formats): JValue = {
  javaType match {
    case JavaType.BOOLEAN => JBool(value.asInstanceOf[Boolean])
    case JavaType.INT => JInt(value.asInstanceOf[Int])
    case JavaType.LONG => JInt(value.asInstanceOf[Long])
    case JavaType.FLOAT => JDouble(value.asInstanceOf[Float])
    case JavaType.DOUBLE => JDouble(value.asInstanceOf[Double])
    case JavaType.STRING => JString(value.asInstanceOf[String])
    case JavaType.BYTE_STRING => 
      // 如果是二进制数据,建议转成Base64字符串,这里用UTF-8示例,可按需替换
      JString(value.asInstanceOf[ByteString].toStringUtf8)
    case JavaType.MESSAGE =>
      // 嵌套消息递归处理
      Extraction.decompose(value.asInstanceOf[Message])(format + new ProtobufJsonSerializer)
    case JavaType.ENUM =>
      JString(value.asInstanceOf[com.google.protobuf.EnumValueDescriptor].getName)
  }
}

3. 序列化Protobuf对象到JSON文件

最后配置Json4s的序列化规则,把Protobuf对象转成JSON字符串并写入文件:

import org.json4s.jackson.Serialization.write
import java.io.PrintWriter

// 配置Json4s序列化格式,加入自定义序列化器
implicit val formats: Formats = Serialization.formats(NoTypeHints) + new ProtobufJsonSerializer

// 把Protobuf对象转成JSON字符串
val jsonStr: String = write(protoObj)

// 将JSON字符串写入文件
new PrintWriter("output.json") {
  write(jsonStr)
  close()
}
额外注意点
  • 如果你的Protobuf里有ByteString类型的二进制数据,建议用Base64编码(需要添加commons-codec依赖),避免UTF-8解析乱码
  • 如果生成的是ScalaPB的Scala类,上面的逻辑同样适用,只需要调整导入的类路径即可
  • 可以根据自己的需求修改序列化器,比如调整字段名的大小写、忽略某些字段等

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 20:47:47