使用 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 对象

使用jackson将json对象列表转换为hashmap

让SpringBoot的jackson支持JavaBean嵌套的protobuf

如何仅使用 Jackson 将 XML 转换为 JSON?

使用 Jackson 将 Java 对象转换为 JSON

JackSon将java对象转换为JSON字符串