使用 Jackson 将 protobuf 转换为 JSON?
Posted
技术标签:
【中文标题】使用 Jackson 将 protobuf 转换为 JSON?【英文标题】:Convert a protobuf to JSON using Jackson? 【发布时间】:2019-01-06 09:32:56 【问题描述】:使用 Jackson 的 ObjectMapper 将 protobuf 转换为 JSON 时出现以下错误:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
Direct self-reference leading to cycle (through reference chain:
MyObjectPb$MyObject["unknownFields"]->
com.google.protobuf.UnknownFieldSet["defaultInstanceForType"])
MyObjectPb 有以下字段:
protected com.google.protobuf.UnknownFieldSet unknownFields
在处理现有代码库时,我有以下限制:
-
我无法修改 MyObjectPb 的源代码,所以我无法在 MyObjectPb 中使用 Jackson 的忽略注解。
我也不能使用 Gson 的库来转换对象,因为代码库已经使用 Jackson 进行序列化。不建议添加新的依赖项。
如何告诉 Jackson 忽略(反)序列化 MyObjectPb 中的 UnknownFieldSet 对象?
我尝试了以下方法,但这些方法似乎无法解决问题:
a) 配置 ObjectMapper:
myObjectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
myObjectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
b) 使用 Jackson Mixin:
@JsonIgnoreType
private abstract class UnknownFieldSetIgnoreMixIn
myObjectMapper.addMixIn(UnknownFieldSet.class, UnknownFieldSetIgnoreMixIn.class)
【问题讨论】:
mixin 是我的第一个猜测。您可能必须声明一个并行类以供 JSON 使用。 MapStruct 可以帮助在那里来回转换。 @chrylis 请问mixin实现是否合适或者我错过了什么?我会查找 MapStruct,非常感谢。 【参考方案1】:我使用了JsonFormat类(com.googlecode.protobuf.format.JsonFormat)来转换protobuf:
new JsonFormat().printToString(myObject)
这对我来说非常完美。
【讨论】:
【参考方案2】:包含已从 com.googlecode.protobuf.format.JsonFormat
更改
到
com.google.protobuf.util.JsonFormat
因此,如果您的 protobuf 依赖项缺少 format
包,请尝试在 util
中查找 JsonFormat
。
有了这个包含,你应该可以使用
new JsonFormat().printToString(myObject)
正如@amad-person 建议的那样。
【讨论】:
【参考方案3】:当前(2018 年 10 月)序列化 protobuf 的方法是按以下方式使用 com.google.protobuf.util.JsonFormat
:
JsonFormat.printer().print(myMessageOrBuilder)
我在我的 protobuf 对象之前使用了 @JsonSerialize(using = MyMessageSerializer.class)
注释并添加了这个类:
public static class MyMessageSerializer extends JsonSerializer<Message>
@Override
public void serialize(Message message, JsonGenerator gen, SerializerProvider serializers) throws IOException
gen.writeRawValue(JsonFormat.printer().print(message));
这允许 new ObjectMapper().writeValueAsString(wrapperObject)
正确地将我的 protobuf 转换为 JSON。
【讨论】:
gen.writeRawValue 如果 protobuf 对象只是 POJO 类中的一个成员 在 sprinboot webflux 中,这个序列化选项/注释有效。唯一的问题是,每次 proto 对象重新生成后,我们都必须编辑生成的 java 代码。 我试过了,但出现以下异常:No serializer found for class com.google.protobuf.UnknownFieldSet$Parser and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: ru.vtb.mmba.account.AccountProto$BankAccountInfo["unknownFields"]
注解@JsonSerialize(using = ProtobufSerializer.class)
好像没用。如果只是将JsonFormat.printer().print(accountInfo)
传递给objectMapper.writeValueAsString
,那么它就可以工作。那么为什么@JsonSerialize(using
会被objectMapper.writeValueAsString
忽略呢?【参考方案4】:
您应该使用 com.google.protobuf.util.JsonFormat
包中的以下类:
JsonFormat.printer().print()
【讨论】:
【参考方案5】:我只是有这个问题,并使用下面的代码从 Message 对象创建 JSON:
JsonFormat.printer().print(getMessageCockroachDB)
【讨论】:
【参考方案6】:这里有一个杰克逊的 protobuf 库:https://github.com/FasterXML/jackson-dataformats-binary
虽然关于如何使用它的文档很少!有一些关于如何使用它进行读/写的示例:https://github.com/FasterXML/jackson-dataformats-binary/blob/2.14/protobuf/src/test/java/com/fasterxml/jackson/dataformat/protobuf/ReadSimpleTest.java#L66
但尚不清楚这将如何放在 GRPC 应用程序中。
【讨论】:
以上是关于使用 Jackson 将 protobuf 转换为 JSON?的主要内容,如果未能解决你的问题,请参考以下文章
使用Jackson – 将字符串转换为 JsonNode 对象