使用Jackson将protobuf转换为JSON?

19

我使用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
作为我正在处理现有的代码库,我有以下限制:
  1. 我不能修改MyObjectPb的源代码,因此无法在MyObjectPb中使用Jackson的忽略注释。
  2. 也不能使用Gson的库来转换对象,因为代码库已经使用Jackson进行序列化。不建议添加新的依赖项。
我该如何告诉Jackson忽略序列化MyObjectPb内部的UnknownFieldSet对象?
我尝试了以下方法,但这些方法似乎无法解决问题:
a) 配置ObjectMapper:
myObjectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
myObjectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
使用Jackson Mixin:

b) 使用Jackson Mixin:

@JsonIgnoreType
private abstract class UnknownFieldSetIgnoreMixIn {}

myObjectMapper.addMixIn(UnknownFieldSet.class, UnknownFieldSetIgnoreMixIn.class)

Mixin 可能是我的第一选择。您可能需要为 JSON 使用声明一个并行类。MapStruct 可以帮助在转换上下文中来回转换。 - chrylis -cautiouslyoptimistic-
@chrylis,能否告诉我mixin实现是否合适或者我有什么遗漏的地方吗? 我会查阅MapStruct的,非常感谢。 - amad-person
8个回答

20

目前(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格式。


3
如果Protobuf对象只是POJO类中的成员,则可以使用gen.writeRawValue。 - Ahmed
1
在Spring Boot WebFlux中,这个序列化选项/注释是有效的。唯一的问题是,在每次Proto对象重建后,我们都必须编辑生成的Java代码。 - Espresso
我尝试了这个,但是出现了以下异常:找不到com.google.protobuf.UnknownFieldSet$Parser类的序列化程序,并且没有发现创建BeanSerializer的属性(为避免异常,请禁用SerializationFeature.FAIL_ON_EMPTY_BEANS)(通过引用链:ru.vtb.mmba.account.AccountProto$BankAccountInfo["unknownFields"] - osya
似乎注释@JsonSerialize(using = ProtobufSerializer.class)没有被使用。如果只是将JsonFormat.printer().print(accountInfo)传递给objectMapper.writeValueAsString,那么它可以工作。那么为什么@JsonSerialize(usingobjectMapper.writeValueAsString忽略了呢? - osya
只是出于好奇,您如何将ProtoBuf消息封装为对象?因为我得到的protobuf输出类似于value:xyz\n,而我期望得到{"value":"xyz"},所以我缺少对象封装。 - Miguel Costa

9
我使用了JsonFormat类(com.googlecode.protobuf.format.JsonFormat)来将protobuf转换为JSON:
new JsonFormat().printToString(myObject)

这对我来说非常完美地完成了工作。

1
我认为printToString已经不存在了。 - Miguel Costa

4

include已经从com.googlecode.protobuf.format.JsonFormat更改为com.google.protobuf.util.JsonFormat

因此,如果您的protobuf依赖项缺少format包,请尝试在util中查找JsonFormat

使用这个include,您应该能够使用

new JsonFormat().printToString(myObject)

正如@amad-person所建议的那样。

4
您应该使用来自包com.google.protobuf.util.JsonFormat的以下类:
JsonFormat.printer().print()

2

我遇到了一个问题,使用下面的代码从Message对象创建JSON:

JsonFormat.printer().print(getMessageCockroachDB)

enter image description here


1

0

我的解决方案使用 @JsonComponent

@JsonComponent
public class MessageSerializer  extends JsonSerializer<Message> {

    @Override
    public void serialize(Message value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeRawValue(JsonFormat.printer().print(value));
    }
}

这也将 Message 类的序列化器注册到 ObjectMapper 中。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接