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

将JSON转换为Protobuf DynamicMessage时Unix时间戳转换失败

问题描述

我正在尝试将JSON字符串动态转换为Protobuf的DynamicMessage类型,使用的Kotlin代码如下:

val topic = "sample-topic"
// 使用Confluent Schema Registry存储/获取Schema
val descriptor: Descriptors.Descriptor = getDescriptorFromRegistry(topic)
// 创建DynamicMessage Builder并传入对应的Descriptor
val builder: DynamicMessage.Builder = DynamicMessage.newBuilder(descriptor)
// 将JSON与Descriptor合并
JsonFormat.parser().merge(json, builder) // 此处抛出异常
val message: DynamicMessage = builder.build()
val bytes: ByteArray = message.toByteArray()

对应的JSON字符串:

{
    "id": 1001,
    "firstName": "Bob",
    "lastName": "Test",
    "email": "bob@test.com",
    "dateCreated": 1696273647694046
}

Protobuf Schema定义:

syntax = "proto3";
package io.smyrgeorge.test.proto.domain;

import "google/protobuf/timestamp.proto";

message Customer {
  int32 id = 1;
  string firstName = 2;
  string lastName = 3;
  string email = 4;
  google.protobuf.Timestamp dateCreated = 5;
}

转换时因dateCreated字段抛出异常,该字段是微秒级Unix时间戳1696273647694046,而Protobuf默认解析器仅支持RFC 3339格式的ISO日期字符串,异常信息如下:

Exception in thread "main" com.google.protobuf.InvalidProtocolBufferException: Failed to parse timestamp: 1696273647694046
at com.google.protobuf.util.JsonFormat$ParserImpl.mergeTimestamp(JsonFormat.java:1564)
at com.google.protobuf.util.JsonFormat$ParserImpl.access$2200(JsonFormat.java:1299)
at com.google.protobuf.util.JsonFormat$ParserImpl$3.merge(JsonFormat.java:1394)
at com.google.protobuf.util.JsonFormat$ParserImpl.merge(JsonFormat.java:1455)
at com.google.protobuf.util.JsonFormat$ParserImpl.parseFieldValue(JsonFormat.java:2018)
at com.google.protobuf.util.JsonFormat$ParserImpl.mergeField(JsonFormat.java:1669)
at com.google.protobuf.util.JsonFormat$ParserImpl.mergeMessage(JsonFormat.java:1500)
at com.google.protobuf.util.JsonFormat$ParserImpl.merge(JsonFormat.java:1458)
at com.google.protobuf.util.JsonFormat$ParserImpl.parseFieldValue(JsonFormat.java:2018)
at com.google.protobuf.util.JsonFormat$ParserImpl.mergeOneofField(JsonFormat.java:1708)
at com.google.protobuf.util.JsonFormat$ParserImpl.mergeField(JsonFormat.java:1667)
at com.google.protobuf.util.JsonFormat$ParserImpl.mergeMessage(JsonFormat.java:1500)
at com.google.protobuf.util.JsonFormat$ParserImpl.merge(JsonFormat.java:1458)
at com.google.protobuf.util.JsonFormat$ParserImpl.merge(JsonFormat.java:1340)
at com.google.protobuf.util.JsonFormat$Parser.merge(JsonFormat.java:472)
at io.smyrgeorge.connect.converter.ProtobufConverter.fromConnectData(ProtobufConverter.kt:93)
at io.smyrgeorge.connect.MainKt.main(Main.kt:54)
at io.smyrgeorge.connect.MainKt.main(Main.kt)
Caused by: java.text.ParseException: Failed to parse timestamp: invalid timestamp "1696273647694046"
at com.google.protobuf.util.Timestamps.parse(Timestamps.java:241)
at com.google.protobuf.util.JsonFormat$ParserImpl.mergeTimestamp(JsonFormat.java:1561)
... 17 more

我希望避免预处理JSON字符串,请问是否可以为Timestamp类型传入自定义解析器?


解决方案

Protobuf官方的JsonFormat.Parser没有直接提供自定义内置类型(如Timestamp)解析逻辑的扩展点,但可以通过先解析JSON为结构化对象,手动处理Timestamp字段,再合并剩余字段的方式实现,无需修改原始JSON字符串:

自定义合并逻辑实现

import com.google.gson.JsonParser
import com.google.protobuf.util.JsonFormat
import com.google.protobuf.util.Timestamps
import com.google.protobuf.Descriptors
import com.google.protobuf.DynamicMessage

fun mergeJsonWithCustomTimestamp(json: String, builder: DynamicMessage.Builder) {
    // 将JSON解析为结构化JsonObject
    val jsonObject = JsonParser.parseString(json).asJsonObject
    
    val descriptor = builder.descriptorForType
    
    // 遍历所有字段,识别并处理Timestamp类型字段
    descriptor.fields.forEach { field ->
        if (field.type == Descriptors.FieldDescriptor.Type.MESSAGE && 
            field.messageType.fullName == "google.protobuf.Timestamp" &&
            jsonObject.has(field.name)) {
            
            // 将微秒时间戳转换为合法的Timestamp对象
            val micros = jsonObject.get(field.name).asLong
            val timestamp = Timestamps.fromMicros(micros)
            // 手动设置到DynamicMessage Builder
            builder.setField(field, timestamp)
            // 移除该字段,避免后续默认merge重复处理
            jsonObject.remove(field.name)
        }
    }
    
    // 合并剩余非Timestamp字段
    JsonFormat.parser().merge(jsonObject.toString(), builder)
}

修改原代码调用自定义方法

val topic = "sample-topic"
val descriptor: Descriptors.Descriptor = getDescriptorFromRegistry(topic)
val builder: DynamicMessage.Builder = DynamicMessage.newBuilder(descriptor)
// 使用自定义方法完成JSON与DynamicMessage的合并
mergeJsonWithCustomTimestamp(json, builder)
val message: DynamicMessage = builder.build()
val bytes: ByteArray = message.toByteArray()

方案说明

  • 无需修改原始JSON字符串,仅在内存中处理结构化的JSON对象
  • 自动识别所有google.protobuf.Timestamp类型的字段,兼容微秒级时间戳转换
  • 剩余字段仍使用Protobuf默认的JSON解析逻辑,保证其他类型字段的解析正确性

内容的提问来源于stack exchange,提问作者Yorgos S.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 05:34:55