Jackson的ContextualSerializer应用场景
创建时间:2015-03-04 15:41
字数:880
阅读:
在某一个项目中,数据库存储图片文件的路径,服务端将域名和路径拼接之后返回给客户端。例如数据库存储的路径为/1.jpg,服务端配置文件中配置的域名为http://img.xxx.com,最终返回给客户端http://img.xxx.com/1.jpg
最简单的方法是在建立 Model(返回给客户端的 JSON 对象)时,直接拼接并赋值,例如:
1 2 3 4 5 6 7 8 9 class UserModel { private String avatar public UserModel (User user) { this.avatar = Config.HOST_NAME + user.getAvatar; } }
但是这样做过于硬编码,不易修改和维护,更好的办法是使用注解显式的声明序列化方式,在 Jackson 中就是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class UserModel { @JsonSerialize (using = ImageURLSerialize.class) private String avatar public UserModel (User user) { this.avatar = user.getAvatar; } } class ImageURLSerialize extends JsonSerializer<String> { @Override public void serialize (String value, JsonGenerator jgen, SerializerProvider provider) throws IOException { if (!StringUtils.isEmpty (value)) { jgen.writeString (Config.HOST_NAME + value); } jgen.writeString (value); } }
这样 Jackson 会使用ImageURLSerialize序列化avatar字段 ,该类实现的serialize方法会自动在路径前添加域名。
可以进一步封装为一个独立的注解使其更容易使用:
1 2 3 4 5 @Retention (RetentionPolicy.RUNTIME) @JacksonAnnotationsInside @JsonSerialize (using = ImageURLSerialize.class) public @interface ImageURL { }
这样在 Model 类中只需要使用@ImageURL注解字段就可以了。
1 2 3 4 5 6 7 8 9 10 class UserModel { @ImageURL private String avatar public UserModel (User user) { this.avatar = user.getAvatar; } }
到此为止处理的都还算不错,直到有一天出现了新需求:图片服务器从本地搬到了阿里云 OSS 上。由于开通了 CDN 服务,图片可以在获取时生成缩略图了,只需要在图片链接后添加类似于@100w_100h的参数。
在之前的基础上,最好的方法肯定是给@ImageURL增加一个字段标识需要附加的参数,例如:
1 2 3 4 5 6 7 8 @Retention (RetentionPolicy.RUNTIME) @JacksonAnnotationsInside @JsonSerialize (using = ImageURLSerialize.class) public @interface ImageURL { // 请求图片附加的参数,会追加在图片地址后面 String param () default ""; }
这时候问题出现了,在ImageURLSerialize的serialize方法中根本无法获得@ImageURL注解,更别说获得注解中的参数了。 接下来就要介绍本文的重点了,ContextualSerializer是 Jackson 提供的另一个序列化相关的接口,它的作用是通过字段已知的上下文信息定制JsonSerializer,只需要实现createContextual方法即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class ImageURLSerialize extends JsonSerializer<String> implements ContextualSerializer { private String param; // 必须要保留无参构造方法 public ImageURLSerialize () { this (""); } public ImageURLSerialize (String param) { this.param = param; } @Override public void serialize (String value, JsonGenerator jgen, SerializerProvider provider) throws IOException { String url = ""; if (!StringUtils.isEmpty (value)) { url = Config.HOST_NAME + value; if (!StringUtils.isEmpty (param)) { url = url + param; } } jgen.writeString (url); } @Override public JsonSerializer<?> createContextual (SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException { if (beanProperty != null) { // 为空直接跳过 if (Objects.equals (beanProperty.getType ().getRawClass (), String.class)) { // 非 String 类直接跳过 ImageURL imageURL = beanProperty.getAnnotation (ImageURL.class); if (imageURL == null) { imageURL = beanProperty.getContextAnnotation (ImageURL.class); } if (imageURL != null) { // 如果能得到注解,就将注解的 value 传入 ImageURLSerialize return new ImageURLSerialize (imageURL.value ()); } } return serializerProvider.findValueSerializer (beanProperty.getType (), beanProperty); } return serializerProvider.findNullValueSerializer (beanProperty); } }
createContextual可以获得字段的类型以及注解。当字段为String类型并且拥有@ImageURL注解时,取出注解中的值创建定制的ImageURLSerialize,这样在serialize方法中便可以得到这个值了。createContextual方法只会在第一次序列化字段时调用(因为字段的上下文信息在运行期不会改变),所以不用担心影响性能。
更多的使用方式可以看 Jackson 官方的测试用例
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 xizhimojie@foxmail.com
文章标题: Jackson的ContextualSerializer应用场景
文章字数: 880
本文作者: yongning
发布时间: 2015-03-04, 15:41:37
最后更新: 2020-12-15, 00:28:36
原始链接: https://getyongning.cn/p/14143.html
版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。