java - 如何使用 Jackson Mixin 将 Json 映射到 Proto?

标签 java json jackson protocol-buffers jackson-databind

我们正在开发一个后端应用程序,其中使用 Protobuffers 作为模型/pojo 文件。 我们必须调用一个 API,它以 JSON 形式返回响应。

我正在寻找一种将 JSON 文件直接映射到 proto 的解决方案。 java 文件。 例如,我们的项目中有示例原型(prototype):

message Example{
 string id                    = 1;
 string another_id            = 2;
 int32 code                   = 3;
 string name                 = 4;
}

现在我们需要调用一个API,它以JSON返回响应:

{
   "json_id":"3",
   "json_another_id":"43",
   "code":34,
   "json_name":"Yeyproto"
}

现在,我想直接使用 Proto 映射响应(json 格式)。 请让我知道该怎么做。请注意,由于Example.java是一个自动生成的java文件,我无法在此类中进行任何更改。另外,请注意json和proto的字段是不同的。

这是我尝试过的。 我尝试使用 Jackson Mixin 并将映射信息保留在 mixin 类中,但它不起作用并引发一些奇怪的 FieldDiscriptor 错误。

public abstract class UserMixin { 
    @JsonProperty("json_id")
    String id;

    @JsonProperty("json_another_id")
    String another_id;

    @JsonProperty("code")
    int code;

    @JsonProperty("json_name")
    String name;
}

和示例用法:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixIn(Example.class, ExampleMixin.class);
Position usr = objectMapper.readerFor(Example.class).readValue(json);
System.out.println(json);

异常(exception):

om.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot find a (Map) Key deserializer for type [simple type, class com.google.protobuf.Descriptors$FieldDescriptor]

请帮我找到解决此问题的好方法。

最佳答案

protoc生成的类编译器并不简单POJO 。它们包含许多不同的方法和类型,我们需要“过滤掉”以制作 Jackson工作。

已修复 MixIn

确实有比自定义解串器更简单的解决方案。您需要忽略 Map<Descriptors.FieldDescriptor, Object> getAllFields()方法并通过添加下划线改进字段名称:_ .

示例:

import com.celoxity.protobuf.ExampleOuterClass.Example;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.google.protobuf.Descriptors;
import com.google.protobuf.Timestamp;

import java.time.Instant;
import java.util.Map;

public class ProtobufApp {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = JsonMapper.builder()
                .enable(SerializationFeature.INDENT_OUTPUT)
                .addMixIn(Example.class, ExampleMixin.class)
                .addMixIn(Timestamp.class, TimestampMixin.class)
                .build();

        String json = "{" +
                "\"json_id\":\"3\"," +
                "\"json_another_id\":\"43\"," +
                "\"code\":34," +
                "\"json_name\":\"Yeyproto\"," +
                "\"currTime\":{\"seconds\":1575909372,\"nanos\":35000000}" +
            "}";
        Example deserialised = mapper.readValue(json, Example.class);

        System.out.println(deserialised);
        Timestamp currTime = deserialised.getCurrTime();
        System.out.println(Instant.ofEpochSecond(currTime.getSeconds(), currTime.getNanos()));
    }
}

abstract class ExampleMixin extends ProtoBufIgnoredMethods {

    @JsonProperty("json_id")
    String id_;

    @JsonProperty("json_another_id")
    String anotherId_;

    @JsonProperty("code")
    int code_;

    @JsonProperty("json_name")
    String name_;

    @JsonProperty("currTime")
    Timestamp currTime_;
}

abstract class TimestampMixin extends ProtoBufIgnoredMethods {
    @JsonProperty("seconds")
    String seconds_;

    @JsonProperty("nanos")
    String nanos_;
}

abstract class ProtoBufIgnoredMethods {
    @JsonIgnore
    public abstract Map<Descriptors.FieldDescriptor, Object> getAllFields();
}

上面的代码打印:

id: "3"
another_id: "43"
code: 34
name: "Yeyproto"
currTime {
  seconds: 1575909372
  nanos: 35000000
}

2019-12-09T16:36:12.035Z

自定义解串器 + com.hubspot图书馆

在这种情况下,最简单的解决方案是为所有 com.google.protobuf.* 编写一组反序列化器和序列化器类型被编译为 POJO 。幸运的是,已经实现了处理它们的模块: jackson-datatype-protobuf .

您的案例中的示例用法可能如下所示:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.hubspot.jackson.datatype.protobuf.ProtobufModule;

import java.io.IOException;

public class ProtobufApp {
    public static void main(String[] args) throws Exception {
        SimpleModule pojosModule = new SimpleModule();
        pojosModule.addDeserializer(Example.class, new ExampleJsonDeserializer());

        ObjectMapper mapper = JsonMapper.builder()
                .enable(SerializationFeature.INDENT_OUTPUT)
                .addModule(new ProtobufModule())
                .addModule(pojosModule)
                .build();

        String json = "{\"json_id\":\"3\",\"json_another_id\":\"43\",\"code\":34,\"json_name\":\"Yeyproto\"}";
        Example deserialised = mapper.readValue(json, Example.class);
        System.out.println(deserialised);
    }
}

class ExampleJsonDeserializer extends JsonDeserializer<Example> {
    @Override
    public Example deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        ObjectNode root = p.readValueAsTree();
        return Example.newBuilder()
                .setId(root.get("json_id").asText())
                .setAnotherId(root.get("json_another_id").asText())
                .setName(root.get("json_name").asText())
                .setCode(root.get("json_id").asInt())
                .build();
    }
}

示例代码打印:

id: "3"
another_id: "43"
code: 3
name: "Yeyproto"

Maven 依赖项:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.10.0</version>
</dependency>
<dependency>
    <groupId>com.google.protobuf</groupId>
    <artifactId>protobuf-java</artifactId>
    <version>3.11.0</version>
</dependency>
<dependency>
    <groupId>com.hubspot.jackson</groupId>
    <artifactId>jackson-datatype-protobuf</artifactId>
    <version>0.9.11-jackson2.9</version>
</dependency>

关于java - 如何使用 Jackson Mixin 将 Json 映射到 Proto?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59157263/

相关文章:

java - 避免声明局部变量

java - 将 Json 数组转换为 Java 数组

javascript - 如何将每个 JSON 行添加到全局 jQuery 变量中?

java - Jackson:如何在序列化过程中对 JsonNode 进行后处理?

java - Jackson 在对丢失的属性(property)进行反序列化期间抛出 NPE

绘制ER图的Java图形二维库?

java - 根据内部日期列表对 JAVA 父列表进行排序

javascript - 读取 php/js 中的 JSON 响应对象

json - 多个Json条目到一个结构

java - 使用 Jackson 反序列化数组