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

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

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

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

咱们先来看看把bean转为json,通常没有特殊要求的话,咱们都是: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。可是他这个重命名并非很灵活,由于他只能固定的重命名为某一个「肯定的」值,而不能允许咱们作一些额外的操做。app

因此在这种状况下,我打算自定义一个注解,由于业务场景相关,咱们的注解定义以下:ide

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

假设咱们的json的key的生成规则以下:ui

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

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

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();
    }

}

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

/**
 * @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

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

还算是很符合咱们的指望的。server

至于server端是如何替换json字符串的key的那块,简单的说一下,由于key有必定的格式,因此能够递归遍历json的全部key,就能够拿到有哪些key-value对须要处理了。关于如何在Java中递归便利Json,这个比较简单。若是你们觉的有须要,我后面在写。

参考资料

  • http://unmi.cc/customize-jackson-annotation-and-disable-specific-annotation/ 虽然咱们并无使用这个文章的作法

相关文章
相关标签/搜索