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,一般没有特殊要求的话,我们都是:

 /**
     * Object可以是POJO,也可以是Collection或数组。
     * 如果对象为Null, 返回"null".
     * 如果集合为空集合, 返回"[]".
     *
     * @param object the object to json
     * @return toJson result
     */
    public String toJson(Object object) {
        try {
            return mapper.writeValueAsString(object);
        } catch (IOException e) {
            LOGGER.error("write to json string error:" + object, e);
            return null;
        }
    }

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

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

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

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Dimension {
    String valueType();
}

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

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

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

import com.google.common.base.Preconditions;
import org.codehaus.jackson.Version;
import org.codehaus.jackson.Versioned;
import org.codehaus.jackson.map.introspect.AnnotatedField;
import org.codehaus.jackson.map.introspect.JacksonAnnotationIntrospector;
import org.codehaus.jackson.util.VersionUtil;

import java.lang.annotation.Annotation;

import static com.google.common.base.Strings.isNullOrEmpty;

/**
 * @author rollenholt
 */
public class DimensionFieldSerializer extends JacksonAnnotationIntrospector implements Versioned {

    @Override
    public Version version() {
        return VersionUtil.versionFor(getClass());
    }


    @Override
    public boolean isHandled(Annotation ann) {
        Class<?> cls = ann.annotationType();
        if (Dimension.class == cls) {
            return true;
        }
        return super.isHandled(ann);
    }


    @Override
    public String findSerializablePropertyName(AnnotatedField af) {
        return getPropertyName(af);
    }

    @Override
    public String findDeserializablePropertyName(AnnotatedField af) {
        return getPropertyName(af);
    }

    private String getPropertyName(AnnotatedField af) {
        Dimension annotation = af.getAnnotation(Dimension.class);
        if (annotation != null) {
            String valueType = annotation.valueType();
            Preconditions.checkArgument(!isNullOrEmpty(valueType), "@Dimension注解中的valudType不能为空");
            if (valueType.equalsIgnoreCase("id")) {
                return af.getName() + "_id";
            }
            if (valueType.equalsIgnoreCase("code")) {
                return af.getName() + "_code";
            }
        }
        return af.getName();
    }

}

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

/**
 * @author rollenholt
 */
public class DimensionAdapterHelper {

    private final static ObjectMapper objectMapper = new ObjectMapper();

    static {
        AnnotationIntrospector dimensionFieldSerializer = new DimensionFieldSerializer();
        objectMapper.setAnnotationIntrospector(dimensionFieldSerializer);
    }

    public static String beanToJson(Object object) {
        StringWriter sw = new StringWriter();
        try {
            objectMapper.writeValue(sw, object);
            return sw.toString();
        } catch (IOException e) {
            throw Throwables.propagate(e);
        }
    }

    public static <T> T jsonToBean(String json, Class<T> clazz) {
        try {
            return (T) objectMapper.readValue(json, clazz);
        } catch (IOException e) {
            throw Throwables.propagate(e);
        }
    }


    public static class Type {
        private String code;

        @Dimension(valueType = "id")
        private String description;

        @Dimension(valueType = "code")
        private String value;

        public Type() {
        }

        public Type(String code, String description, String value) {
            super();
            this.code = code;
            this.description = description;
            this.value = value;
        }

        public String getCode() {
            return code;
        }

        public void setCode(String code) {
            this.code = code;
        }

        public String getDescription() {
            return description;
        }

        public void setDescription(String description) {
            this.description = description;
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }

        @Override
        public String toString() {
            return ToStringBuilder.reflectionToString(this);
        }
    }

    public static void main(String[] args) {
        Type t = new Type("a", "b", "c");
        String json = beanToJson(t);
        System.out.println(json);
        Type type = jsonToBean(json, Type.class);
        System.out.println(type);

    }


}

运行之后输出结果为:

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

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

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

参考资料

目录
相关文章
|
19天前
|
JSON Java 数据格式
springboot中表字段映射中设置JSON格式字段映射
springboot中表字段映射中设置JSON格式字段映射
51 1
|
13天前
|
JSON JavaScript Java
对比JSON和Hessian2的序列化格式
通过以上对比分析,希望能够帮助开发者在不同场景下选择最适合的序列化格式,提高系统的整体性能和可维护性。
17 3
|
18天前
|
JSON 数据格式 索引
Python中序列化/反序列化JSON格式的数据
【11月更文挑战第4天】本文介绍了 Python 中使用 `json` 模块进行序列化和反序列化的操作。序列化是指将 Python 对象(如字典、列表)转换为 JSON 字符串,主要使用 `json.dumps` 方法。示例包括基本的字典和列表序列化,以及自定义类的序列化。反序列化则是将 JSON 字符串转换回 Python 对象,使用 `json.loads` 方法。文中还提供了具体的代码示例,展示了如何处理不同类型的 Python 对象。
|
29天前
|
JSON 人工智能 算法
探索LLM推理全阶段的JSON格式输出限制方法
文章详细讨论了如何确保大型语言模型(LLMs)输出结构化的JSON格式,这对于提高数据处理的自动化程度和系统的互操作性至关重要。
|
15天前
|
JSON API 数据安全/隐私保护
拍立淘按图搜索API接口返回数据的JSON格式示例
拍立淘按图搜索API接口允许用户通过上传图片来搜索相似的商品,该接口返回的通常是一个JSON格式的响应,其中包含了与上传图片相似的商品信息。以下是一个基于淘宝平台的拍立淘按图搜索API接口返回数据的JSON格式示例,同时提供对其关键字段的解释
|
1月前
|
JSON JavaScript Java
在Java中处理JSON数据:Jackson与Gson库比较
本文介绍了JSON数据交换格式及其在Java中的应用,重点探讨了两个强大的JSON处理库——Jackson和Gson。文章详细讲解了Jackson库的核心功能,包括数据绑定、流式API和树模型,并通过示例演示了如何使用Jackson进行JSON解析和生成。最后,作者分享了一些实用的代码片段和使用技巧,帮助读者更好地理解和应用这些工具。
在Java中处理JSON数据:Jackson与Gson库比较
|
1月前
|
JSON API 数据格式
商品详情数据JSON格式示例参考(api接口)
JSON数据格式的商品详情数据通常包含商品的多个层级信息,以下是一个综合多个来源信息的JSON数据格式的商品详情数据示例参考:
|
2月前
|
XML 存储 JSON
Twaver-HTML5基础学习(19)数据容器(2)_数据序列化_XML、Json
本文介绍了Twaver HTML5中的数据序列化,包括XML和JSON格式的序列化与反序列化方法。文章通过示例代码展示了如何将DataBox中的数据序列化为XML和JSON字符串,以及如何从这些字符串中反序列化数据,重建DataBox中的对象。此外,还提到了用户自定义属性的序列化注册方法。
47 1
|
1月前
|
数据采集 JSON 数据处理
抓取和分析JSON数据:使用Python构建数据处理管道
在大数据时代,电商网站如亚马逊、京东等成为数据采集的重要来源。本文介绍如何使用Python结合代理IP、多线程等技术,高效、隐秘地抓取并处理电商网站的JSON数据。通过爬虫代理服务,模拟真实用户行为,提升抓取效率和稳定性。示例代码展示了如何抓取亚马逊商品信息并进行解析。
抓取和分析JSON数据:使用Python构建数据处理管道
|
22天前
|
JSON 缓存 前端开发
PHP如何高效地处理JSON数据:从编码到解码
在现代Web开发中,JSON已成为数据交换的标准格式。本文探讨了PHP如何高效处理JSON数据,包括编码和解码的过程。通过简化数据结构、使用优化选项、缓存机制及合理设置解码参数等方法,可以显著提升JSON处理的性能,确保系统快速稳定运行。