如何在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
相关产品推荐
相关产品推荐

