问题背景

REST 项目使用protobuf 来加速项目开发,定义了很多model,vo,最终返回的仍然是JSON.

项目中一般使用 一个Response类,

public class Response<T> {
int code;
String message;
T data;
}

如果需要分页,则还需要如下的类

public class Pagedata<T> {
long totalcount;
List<T> datas;
}

那么在Controller中,直接返回

Response

.set( Pagedata. set ( Protobuf类 ) )

这种形式,会被Spring的HttpMessageConverter 识别为 Response类,而不是protobuf类,因此选择了正常的 jackson MessageConverter。

这个时候,会报错:

Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Direct self-reference leading to cycle (through reference chain: com.xxx.crm.proto.xxxx["unknownFields"]-

由此可见 jackson 不支持Protobuf类的JSON序列化。

解决方案

思路一

如果希望被HttpMessageConverter 正确选择 ProtobufJsonFormatHttpMessageConverter,那么整个类都应该是Protobuf的类。那么要使用

如下的写法:

import "google/protobuf/any.proto";

option java_outer_classname = "ResponseProto";

message ProtoResponse {
int32 code = 1;
string message = 2;
ProtoPagedData data = 3;
} message ProtoPagedData {
repeated google.protobuf.Any datas = 1;
int64 totalcount = 2;
}

不管什么类都需要用此Protobuf类来 pack。

@GetMapping(value = "/someUrl")
public Object handler() { List<FooBarProtobufVO> data = //
ResponseProto.ProtoResponse ok = ResponseProto.ProtoResponse.newBuilder()
.setCode(0)
.setMsg("ok")
.setData(ResponseProto.ProtoPagedData.newBuilder()
.addAllDatas(data.stream().map(Any::pack).collect(Collectors.toList()))
.setTotalcount(all.getTotalElements())
.build())
.build();
return ok;
}

注意:如果使用Any需要使用TypeRegistry显式注册你的实际类型,否则使用JsonFormat.printer().print打印的时候,会报错:Cannot find type for url: type.googleapis.com

这个方式最终是通过ProtobufJsonFormatHttpMessageConverter序列化的。

(我的另一篇文章也指出了,HttpMessageConverter的顺序十分重要,这里需要让ProtobufJsonFormatHttpMessageConverter 在系统的靠前的位置)

思路二

既然protobuf的类不能被jackson正确序列化,那么直接返回一个String,或许使用 JsonFormat也是一个不错的选择。

JsonFormat.printer()
.omittingInsignificantWhitespace()
.preservingProtoFieldNames()
.includingDefaultValueFields()
.print(messageOrBuilder);

通过 JsonFormat打印出protobuf JSON形式,但是这个的缺陷是 JsonFormat不支持 list 的 Protobuf类,仅支持单个的protobuf类。

那么只能按照思路一的方式把他套进一个repeated 的 proto中。

得到JSON之后,如果又希望能灵活的往数据结构中增加字段,例如 code/msg/data/ 这种形式,不满足,还需要增加某些临时的字段例如 successCount, totalCount, errorCount 等等

这个时候,还需要用FASTJSON 再将这个字符串使用JSON.parseObject 得到 一个 JSONObject,再添加一些字段。这样比较麻烦,但是也能解决问题。

这种情况返回给HttpMessageConverter处理的是String,因此最终会被StringHttpMessageConverter序列化。

(为了严谨,这里因为是StringHttpMessageConverter处理,那么ResponseHeader 的Content-Type是 text/plain;charset=UTF-8,严格来讲,如果客户端没有正确识别这个JSON字符串,因此还需要在Controller的方法上面,增加额外的produces = MediaType.APPLICATION_JSON_UTF8_VALUE )

思路三

jackson那么强大,直接让jackson支持protobuf行不行?

答案是行。

找到jackson的 github项目页面

然后 发现,readme下方有

jackson-datatype-protobuf for handling datatypes defined by the standard Java protobuf library, developed by HubSpot

NOTE! This is different from jackson-dataformat-protobuf which adds support for encoding/decoding protobuf content but which does NOT depend on standard Java protobuf library

点进入查看 jackson-datatype-protobuf

Jackson module that adds support for serializing and deserializing Google's Protocol Buffers to and from JSON.

Usage

Maven dependency

To use module on Maven-based projects, use following dependency:

<dependency>
<groupId>com.hubspot.jackson</groupId>
<artifactId>jackson-datatype-protobuf</artifactId>
<version><!-- see table below --></version>
</dependency>

那么怎么集成到SpringBoot中呢?

  1. 引入上述第三方jackson-datatype-protobuf的依赖
  2. 在项目中引入ProtobufModule。
@Configuration
public class JacksonProtobufSupport { @Bean
@SuppressWarnings("unchecked")
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return jacksonObjectMapperBuilder -> {
jacksonObjectMapperBuilder.featuresToDisable(
JsonGenerator.Feature.IGNORE_UNKNOWN,
MapperFeature.DEFAULT_VIEW_INCLUSION,
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
);
jacksonObjectMapperBuilder.propertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE);//如果字段都是驼峰命名规则,需要这一句
jacksonObjectMapperBuilder.modulesToInstall(ProtobufModule.class);
};
} }

完美解决

让SpringBoot的jackson支持JavaBean嵌套的protobuf的更多相关文章

  1. 让jquery easyui datagrid列支持绑定嵌套对象

    嵌套对象是指返回的json数据,是对象的某个属性自带有属性.而我们恰恰又需要这个属性,默认情况下easyui的datagrid是不支持绑定嵌套对象的.比如:datagrid的field属性只能为fie ...

  2. springboot之jackson的两种配置方式

    springboot 针对jackson是自动化配置的,如果需要修改,有两种方式: 方式一:通过application.yml 配置属性说明:## spring.jackson.date-format ...

  3. springboot 配置jsp支持

      springboot默认并不支持jsp模板,所以需要配置. 下面是一个可以运行的例子: 首先配置属性文件: spring.http.encoding.force=true spring.http. ...

  4. Springboot中IDE支持两种打包方式,即jar包和war包

    Springboot中IDE支持两种打包方式,即jar包和war包 打包之前修改pom.xml中的packaging节点,改为jar或者war    在项目的根目录执行maven 命令clean pa ...

  5. SpringBoot 多数据库支持:

    SpringBoot 多数据库支持: springboot2.0+mybatis多数据源集成 https://www.cnblogs.com/cdblogs/p/9275883.html Spring ...

  6. SpringBoot与jackson.databind兼容报错问题

    SpringBoot与jackson.databind兼容报错问题 ———————————————— 1.SpringBoot版本V2.0.0其依赖的jackson-databind版本为V2.9.4 ...

  7. Springboot(二)springboot之jsp支持

    参考恒宇少年的博客:https://www.jianshu.com/p/90a84c814d0c springboot内部对jsp的支持并不是特别理想,而springboot推荐的视图是Thymele ...

  8. 源码分析springboot自定义jackson序列化,默认null值个性化处理返回值

    最近项目要实现一种需求,对于后端返回给前端的json格式的一种规范,不允许缺少字段和字段值都为null,所以琢磨了一下如何进行将springboot的Jackson序列化自定义一下,先看看如何实现,再 ...

  9. SpringBoot系列——Jackson序列化

    前言 Spring Boot提供了与三个JSON映射库的集成: Gson Jackson JSON-B Jackson是首选的默认库. 官网介绍: https://docs.spring.io/spr ...

随机推荐

  1. ASP.NET Core 3.0 : 二十四. 配置的Options模式

    上一章讲到了配置的用法及内部处理机制,对于配置,ASP.NET Core还提供了一种Options模式.(ASP.NET Core 系列目录) 一.Options的使用 上一章有个配置的绑定的例子,可 ...

  2. WordPress后台地址路径修改方法

    用过WordPress后台的,其实都知道http://域名目录/wp-login.php就是登录地址,如果这时候使用暴力破解,很可能破解密码(这就有些想象力了),下面芝麻带你看看如何自定义美观的地址. ...

  3. Django模板语言 标签整理

    Django模板语言 标签 内置标签引用 1. autoescape 控制自动转义是否可用. 这种标签带有任何 on 或 off 作为参数的话,他将决定转义块内效果. 该标签会以一个endautoes ...

  4. jmeter入门(环境搭建&运行&初识)

    最近了解了一些性能测试的基础知识和原理决定动手实践下,比较选择了jmeter 一.什么是jmeter Apache JMeter是Apache组织开发的基于Java的压力测试工具.用于对软件做压力测试 ...

  5. 字符串转数字(with Java)

    1. 字符串中提取数字 两个函数可以帮助我们从字符串中提取数字(整型.浮点型.字符型...). parseInt().parseFloat() valueOf() String str = " ...

  6. Hadoop源代码点滴-基础概念

    大数据特征:volume(数量).variety(多样性).velocity(产生的速度) 大数据特征:多.乱.快.杂 数据的来源:业务数据.日志.管理文档(OCR).互联网.物联网.外购

  7. Marshmallow权限使用

    Google发布Android 6.0后对用权限的控制更加严格,在Android5.1或更低的版本中用户能在App的安装期间或使用设置应用程序权限来同意或拒绝某个权限,而在Android6.0或更高的 ...

  8. SUSE CaaS Platform 4 - 安装部署

    SUSE CaaS Platform 相关文章 (1)SUSE CaaS Platform 4 - 简介 (2)SUSE CaaS Platform 4 - 安装部署 (3)SUSE CaaS Pla ...

  9. ServiceStack.Redis高效封装和简易破解

    1.ServiceStack.Redis封装 封装的Redis操作类名为RedisHandle,如下代码块(只展示部分代码),它的特点: 1)使用连接池管理连接,见代码中的PooledClientMa ...

  10. 实现一个3D图片轮播插件 —— 更新版

    前言:     前段时间写下了之前那篇 3D图片轮播效果,后来发现了 Pedro Botelho 写的jquery.gallery.js ,于是重新修改了自己的这个图片轮播,使之可以成为一个插件来使用 ...