Jackson 通过自定义注解来控制json key的格式

最近我这边有一个需求就是需要把Bean中的某一些特殊字段的值进行替换。而这个替换过程是需要依赖一个第三方的dubbo服务的。为了使得这个转换功能更加的通用,我们采用了下面的方式:

  • client端使用自定义的注解(假设为@Dimension)标记Bean中所有的「特殊字段」
  • client端把bean转换为json格式,但是这个转换过程的要求是:这些特殊的字段对应的json的key需要符合一定的格式,而这个格式依赖于标记的@Dimension注解
  • 然后client端通过dubbo RPC服务把json扔给server端,server进行一些json解析,替换之后把替换之后的json扔给client端,然后client端把接收到的json再转回为之前的Bean对象的实例。

我们先来看看把bean转为json,一般没有特殊要求的话,我们都是:

  1. /**
  2. * Object可以是POJO,也可以是Collection或数组。
  3. * 如果对象为Null, 返回"null".
  4. * 如果集合为空集合, 返回"[]".
  5. *
  6. * @param object the object to json
  7. * @return toJson result
  8. */
  9. public String toJson(Object object) {
  10. try {
  11. return mapper.writeValueAsString(object);
  12. } catch (IOException e) {
  13. LOGGER.error("write to json string error:" + object, e);
  14. return null;
  15. }
  16. }

这种是默认的情况,生成的json的key和对应的Bean的filed的name是一模一样的。

而Jackson也给我们提供了注解:@JsonProperty注解来帮助我们重命名生成的json的key。但是他这个重命名并不是很灵活,因为他只能固定的重命名为某一个「确定的」值,而不能容许我们做一些额外的操作。

所以在这种情况下,我打算自定义一个注解,因为业务场景相关,我们的注解定义如下:

  1. @Documented
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target(ElementType.FIELD)
  4. public @interface Dimension {
  5. String valueType();
  6. }

假设我们的json的key的生成规则如下:

  • valueType()的值为“id”时,json key追加后缀“_id”
  • valueType()的值为"code"时,json key追加后缀“_code”

这个时候我们就可以使用Jackson提供给我们强大的JacksonAnnotationIntrospector类了。

  1. import com.google.common.base.Preconditions;
  2. import org.codehaus.jackson.Version;
  3. import org.codehaus.jackson.Versioned;
  4. import org.codehaus.jackson.map.introspect.AnnotatedField;
  5. import org.codehaus.jackson.map.introspect.JacksonAnnotationIntrospector;
  6. import org.codehaus.jackson.util.VersionUtil;
  7. import java.lang.annotation.Annotation;
  8. import static com.google.common.base.Strings.isNullOrEmpty;
  9. /**
  10. * @author rollenholt
  11. */
  12. public class DimensionFieldSerializer extends JacksonAnnotationIntrospector implements Versioned {
  13. @Override
  14. public Version version() {
  15. return VersionUtil.versionFor(getClass());
  16. }
  17. @Override
  18. public boolean isHandled(Annotation ann) {
  19. Class<?> cls = ann.annotationType();
  20. if (Dimension.class == cls) {
  21. return true;
  22. }
  23. return super.isHandled(ann);
  24. }
  25. @Override
  26. public String findSerializablePropertyName(AnnotatedField af) {
  27. return getPropertyName(af);
  28. }
  29. @Override
  30. public String findDeserializablePropertyName(AnnotatedField af) {
  31. return getPropertyName(af);
  32. }
  33. private String getPropertyName(AnnotatedField af) {
  34. Dimension annotation = af.getAnnotation(Dimension.class);
  35. if (annotation != null) {
  36. String valueType = annotation.valueType();
  37. Preconditions.checkArgument(!isNullOrEmpty(valueType), "@Dimension注解中的valudType不能为空");
  38. if (valueType.equalsIgnoreCase("id")) {
  39. return af.getName() + "_id";
  40. }
  41. if (valueType.equalsIgnoreCase("code")) {
  42. return af.getName() + "_code";
  43. }
  44. }
  45. return af.getName();
  46. }
  47. }

同时为了触发上面的代码,以及为了验证我们的功能,我们有如下的代码:

  1. /**
  2. * @author rollenholt
  3. */
  4. public class DimensionAdapterHelper {
  5. private final static ObjectMapper objectMapper = new ObjectMapper();
  6. static {
  7. AnnotationIntrospector dimensionFieldSerializer = new DimensionFieldSerializer();
  8. objectMapper.setAnnotationIntrospector(dimensionFieldSerializer);
  9. }
  10. public static String beanToJson(Object object) {
  11. StringWriter sw = new StringWriter();
  12. try {
  13. objectMapper.writeValue(sw, object);
  14. return sw.toString();
  15. } catch (IOException e) {
  16. throw Throwables.propagate(e);
  17. }
  18. }
  19. public static <T> T jsonToBean(String json, Class<T> clazz) {
  20. try {
  21. return (T) objectMapper.readValue(json, clazz);
  22. } catch (IOException e) {
  23. throw Throwables.propagate(e);
  24. }
  25. }
  26. public static class Type {
  27. private String code;
  28. @Dimension(valueType = "id")
  29. private String description;
  30. @Dimension(valueType = "code")
  31. private String value;
  32. public Type() {
  33. }
  34. public Type(String code, String description, String value) {
  35. super();
  36. this.code = code;
  37. this.description = description;
  38. this.value = value;
  39. }
  40. public String getCode() {
  41. return code;
  42. }
  43. public void setCode(String code) {
  44. this.code = code;
  45. }
  46. public String getDescription() {
  47. return description;
  48. }
  49. public void setDescription(String description) {
  50. this.description = description;
  51. }
  52. public String getValue() {
  53. return value;
  54. }
  55. public void setValue(String value) {
  56. this.value = value;
  57. }
  58. @Override
  59. public String toString() {
  60. return ToStringBuilder.reflectionToString(this);
  61. }
  62. }
  63. public static void main(String[] args) {
  64. Type t = new Type("a", "b", "c");
  65. String json = beanToJson(t);
  66. System.out.println(json);
  67. Type type = jsonToBean(json, Type.class);
  68. System.out.println(type);
  69. }
  70. }

运行之后输出结果为:

  1. {"code":"a","description_id":"b","value_code":"c"}
  2. DimensionAdapterHelper$Type@2cb4c3ab[code=a,description=b,value=c]

还算是很符合我们的期望的。

至于server端是如何替换json字符串的key的那块,简单的说一下,因为key有一定的格式,所以可以递归遍历json的所有key,就可以拿到有哪些key-value对需要处理了。关于如何在Java中递归便利Json,这个比较简单。如果大家觉的有需要,我后面在写。

参考资料

Jackson 通过自定义注解来控制json key的格式的更多相关文章

  1. C#应用Newtonsoft.Json.dll,控制json的时间格式

    原文:C#应用Newtonsoft.Json.dll,控制json的时间格式 var aIsoDateTimeConverter = new IsoDateTimeConverter();aIsoDa ...

  2. Json解析工具Jackson(使用注解)--jackson框架自定义的一些json解析注解

    Json解析工具Jackson(使用注解)--jackson框架自定义的一些json解析注解 @JsonIgnoreProperties 此注解是类注解,作用是json序列化时将Javabean中的一 ...

  3. SpringBoot:自定义注解实现后台接收Json参数

    0.需求 在实际的开发过程中,服务间调用一般使用Json传参的模式,SpringBoot项目无法使用@RequestParam接收Json传参 只有@RequestBody支持Json,但是每次为了一 ...

  4. 属性序列化自定义与字母表排序-JSON框架Jackson精解第3篇

    Jackson是Spring Boot默认的JSON数据处理框架,但是其并不依赖于任何的Spring 库.有的小伙伴以为Jackson只能在Spring框架内使用,其实不是的,没有这种限制.它提供了很 ...

  5. SpringCloud微服务实战——搭建企业级开发框架(三十九):使用Redis分布式锁(Redisson)+自定义注解+AOP实现微服务重复请求控制

      通常我们可以在前端通过防抖和节流来解决短时间内请求重复提交的问题,如果因网络问题.Nginx重试机制.微服务Feign重试机制或者用户故意绕过前端防抖和节流设置,直接频繁发起请求,都会导致系统防重 ...

  6. struts2拦截器加自定义注解实现权限控制

    https://blog.csdn.net/paul342/article/details/51436565 今天结合Java的Annotation和Struts2进行注解拦截器权限控制. 功能需求: ...

  7. 使用spring aspect控制自定义注解

    自定义注解:这里是一个处理异常的注解,当调用方法发生异常时,返回异常信息 /** * ErrorCode: * * @author yangzhenlong * @since 2016/7/21 */ ...

  8. 在springMVC中使用自定义注解来进行登录拦截控制

    1:java注解使用是相当频繁,特别是在搭建一些框架时,用到类的反射获取方法和属性,用的尤其多. java中元注解有四个: @Retention     @Target     @Document  ...

  9. ssm+redis 如何更简洁的利用自定义注解+AOP实现redis缓存

    基于 ssm + maven + redis 使用自定义注解 利用aop基于AspectJ方式 实现redis缓存 如何能更简洁的利用aop实现redis缓存,话不多说,上demo 需求: 数据查询时 ...

随机推荐

  1. 如何为eclipse安装合适版本的python插件pydev

    pydev是一款优秀的Eclipse插件,大多数喜欢在eclipse开发软件的程序员(也许是java程序员)在开发python软件时希望继续使用eclipse,那么pydev是非常理想的选择. 1.安 ...

  2. jQuery css3仿游戏网站右键环形菜单

    效果展示 http://hovertree.com/texiao/jquery/86/ PC用户右键弹出环形菜单. 手机用户扫描二维码: 长安可以弹出环形菜单. 转自:http://hovertree ...

  3. 浅谈Hybrid技术的设计与实现第三弹——落地篇

    前言 接上文:(阅读本文前,建议阅读前两篇文章先) 浅谈Hybrid技术的设计与实现 浅谈Hybrid技术的设计与实现第二弹 根据之前的介绍,大家对前端与Native的交互应该有一些简单的认识了,很多 ...

  4. Android中通信协议

    一.TCP/IP协议(传输层协议) 1.Socket与ServerSocket Socket是应用层与TCP/IP协议簇通讯的中间抽象层,Socket是一组接口,在设计模式中,Socket的设计就是门 ...

  5. 读《C#高级编程》第1章问题

    读<C#高级编程>第1章 .Net机构体系笔记 网红的话:爸爸说我将来会是一个牛逼的程序员,因为我有一个梦,虽然脑壳笨但是做事情很能坚持. 本章主要是了解.Net的结构,都是一些概念,并没 ...

  6. nodejs pm2部署配置

    pm2是一个进程管理工具,可以用它来管理你的node进程,并查看node进程的状态,当然也支持性能监控,进程守护,负载均衡等功能. 1.pm2安装使用需要全局安装  npm install -g pm ...

  7. 用lumen构建API的相关流程

    概述 Lumen是一个基于Laravel的微框架,主要用于小型应用和微服务,专注于性能和速度的优化,该框架一个重要的应用就是构建 RESTAPI. 为什么用Lumen构建REST API Lumen访 ...

  8. ubuntu 常见错误--Could not get lock /var/lib/dpkg/lock

    ubuntu 常见错误--Could not get lock /var/lib/dpkg/lock 通过终端安装程序sudo apt-get install xxx时出错:E: Could not ...

  9. [Hadoop in Action] 第4章 编写MapReduce基础程序

    基于hadoop的专利数据处理示例 MapReduce程序框架 用于计数统计的MapReduce基础程序 支持用脚本语言编写MapReduce程序的hadoop流式API 用于提升性能的Combine ...

  10. mkdir,rmdir,cp,rm,mv,cat,touch用法

    一.mkdir新建目录 1.进入tmp目录,查看该目录下面的子目录 [root@localhost ~]# cd /tmp[root@localhost tmp]# lshsperfdata_root ...