Jackson 序列号字典字段属性

Jackson 序列号字典字段属性,第1张

Jackson 序列号字典字段属性

Jackson 序列号字典字段属性

前言场景正文

一 添加配置二 添加项目内省器三 自定义序列化规则四 字典缓存工具五 测试对象六 测试接口七 测试结果 总结

前言

在前后端分离的项目中,存在很多字段是下拉框选项的情况。
比如:性别字段

代码值value显示值label1男0女2其他

在数据库中,存的是代码值,前端显示的时候,需要显示值。这时候,在返回的时候,就需要做一次代码转换。
(为什么不是前端自己转换?为了统一管理和方便变更。)

场景

字典字段为空,则不转换。一个对象中的多个字段是字典字段。对象中的子对象也有字典字段。对象的子对象是集合,也存在字典字段。 正文

因为在业务开发过程中,存在非常多的这种实体,所以需要制定一个统一处理。减少业务开发的工作量。
(最终结果,减少到只需要一行注解)

一 添加配置

项目中都有 fasterxml 的依赖吧,没有自己添加。
添加类 JacksonConfig,用于配置自定义内省器。

import com.cah.project.conf.serializer.ProjectJacksonAnnotationIntrospector;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;


@Configuration
public class JacksonConfig {

    public JacksonConfig() {}

    @Bean
    @Primary
    @ConditionalOnMissingBean({ObjectMapper.class})
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
        // 定义 不为空,才能进行序列化
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // 添加 自定义 项目注解内省器
        objectMapper.setAnnotationIntrospector(new ProjectJacksonAnnotationIntrospector());
        return objectMapper;
    }

}
二 添加项目内省器
import com.cah.project.core.annotation.Dict;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;


public class ProjectJacksonAnnotationIntrospector extends JacksonAnnotationIntrospector {

    public ProjectJacksonAnnotationIntrospector() {}

    @Override
    public Object findSerializer(Annotated a) {
        // 如果是字典注解
        Dict dict = _findAnnotation(a, Dict.class);
        if(dict != null) {
            return new DictSerializer(dict.type(), dict.separator());
        }
        // 其他扩展。。。
        return super.findSerializer(a);
    }

}

其中 Dict 注解类 为自定义注解。内容如下:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Dict {

    
    String type();
    
    String desc() default "";
    
    String separator() default ",";

}
三 自定义序列化规则
import com.cah.project.core.cache.DictCacheUtil;
import com.cah.project.core.domain.bo.DictData;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

import java.io.IOException;


public class DictSerializer extends StdSerializer {

    
    private final String type;
    
    private final String separator;

    public DictSerializer(String type, String separator) {
        super(Object.class);
        this.type = type;
        this.separator = separator;
    }

    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeObject(this.getLabel(value.toString()));
    }

    
    private DictData getLabel(String code) {
        DictData dd = new DictData();
        dd.setType(this.type);
        dd.setValue(code);
        if(this.separator != null && this.separator.length() > 0) {
            String[] strs = code.split(this.separator);
            StringBuilder sb = new StringBuilder();
            for(String str : strs) {
                // 从缓存中获取字典。如果不行,通过SpringUtil.getBean(); 获取服务处理
                sb.append(DictCacheUtil.getLabel(this.type, str)).append(this.separator);
            }
            dd.setLabel(sb.substring(0, sb.length() - this.separator.length() + 1));
        } else {
            // 从缓存中获取字典。如果不行,通过SpringUtil.getBean(); 获取服务处理
            dd.setLabel(DictCacheUtil.getLabel(this.type, code));
        }
        return dd;
    }

}

 
四 字典缓存工具 

字典缓存工具,使用的是hutool工具包中的缓存。
至于缓存如何清理与同步,这是另外一个问题。也可以从redis中获取缓存。
缓存是为了加快转换速度。各自发挥就好了。

import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheUtil;
import com.cah.project.core.domain.bo.DictData;

import java.util.HashMap;
import java.util.Map;


public class DictCacheUtil {

    
    protected static Cache> dictCache = CacheUtil.newLFUCache(10000);

    
    public static void put(String type, DictData dd) {
        Map map = dictCache.get(type);
        if(map == null) {
            map = new HashMap<>();
            dictCache.put(type, map);
        }
        map.put(dd.getValue(), dd);
    }

    
    public static DictData get(String type, String value) {
        Map map = dictCache.get(type);
        if(map == null) {
            return new DictData(type, value, value);
        }
        return map.getOrDefault(value, new DictData(type, value, value));
    }

    
    public static String getLabel(String type, String value) {
        Map map = dictCache.get(type);
        if(map == null) {
            return value;
        }
        DictData dd = map.get(value);
        return dd != null ? dd.getLabel() : value;
    }

}

五 测试对象

测试对象为返回的实体对象。其中字段字段都添加了 @Dict 注解

import com.cah.project.core.annotation.Dict;
import lombok.Data;

import java.io.Serializable;
import java.util.List;

@Data
public class TestOutVO implements Serializable {

    private String name;

    @Dict(type = "STATUS_CD")
    private String status;

    @Dict(type = "SEX_CD")
    private String sex;

    private List children;

}
import com.cah.project.core.annotation.Dict;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ChildOutVO {

    private String name;

    @Dict(type = "TYPE_CD")
    private Integer type;

    @Dict(type = "SEX_CD")
    private String sex;

}
六 测试接口
import cn.hutool.core.collection.ListUtil;
import com.cah.project.core.domain.out.CommonResult;
import com.cah.project.test.out.ChildOutVO;
import com.cah.project.test.out.TestOutVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;


@Api(tags = {"测试 控制层"})
@RestController
@RequestMapping("/test")
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
public class TestController {

    @GetMapping("getById")
    @ApiOperation(value="测试获取数据")
    public CommonResult getById(@RequestParam("id") String id){
        TestOutVO outVO = new TestOutVO();
        outVO.setName("名称");
        outVO.setStatus("1");
        outVO.setSex("0,1");
        ChildOutVO c1 = ChildOutVO.builder().name("子1").type(2).sex("0").build();
        ChildOutVO c2 = ChildOutVO.builder().name("子2").type(3).sex("1").build();
        List list = ListUtil.toList(c1, c2);
        outVO.setChildren(list);
        return CommonResult.data(outVO);
    }

}
七 测试结果

总结

通过以上的方式,可以解放业务开发过程中反复的字典映射 *** 作。但是也存在一些问题。比如复杂的转换,非缓存数据转换等。
对于以上问题,可以使用 @JsonSerialize(using = XxxSerializer.class)
的方式进行处理。

代码地址

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5719536.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)