Springboot序列化动态增加字段

前言

我们使用类型时,返回前段一般需要加一些字段来标识这个类型的中文名。但是这个需要我们手动添加一个字段,并且在返回时给这个中文字段设置值,很麻烦那有没有更方便的做法呢。当然有的,下边我们来学习一下序列化动态增加字段。

开始操作

环境:
JDK 1.8 , Spring boot 2.4.3, fastjson 1.2.76
首先我们需要定义一个枚举,来标识我们哪些字段需要扩展中文名称字段

/**
 * 功能描述 : 注解序列化
 *
 * @author ziyear 2021-12-18 19:21
 */
@Inherited
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface EnumSerialize {

    /**
     * 字段对应枚举的class
     *
     * @return
     */
    Class<?> value();

    /**
     * 获取中文名称的方法
     *
     * @return
     */
    String method() default "getMsgByCode";

}

我们再去创建两个枚举吧
用户性别枚举

/**
 * 功能描述 : 性别枚举
 *
 * @author ziyear 2021-12-18 19:21
 */
public enum SexEnum {
    MAN("man", "男"),
    WOMAN("woman", "女"),
    ;
    private final String code;
    private final String msg;

    public String getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

    SexEnum(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }

	/**
	 * 根据code获取中文描述的方法
	 */
    public static String getMsgByCode(String code) {
        for (SexEnum sexEnum : values()) {
            if (sexEnum.code.equals(code)) {
                return sexEnum.msg;
            }
        }
        return code;
    }
}

用户级别枚举

/**
 * 功能描述 : 会员级别
 *
 * @author ziyear 2021-12-18 19:22
 */
public enum UserLevelEnum {
    DEFAULT("default", "普通会员"),
    ADVANCED("advanced", "高级会员"),
    SUPER("super", "至尊会员"),
    ;
    private final String code;
    private final String msg;

    public String getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

    UserLevelEnum(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    /**
     * 根据code取名称
     */
    public static String getMsg(String code) {
        for (UserLevelEnum anEnum : values()) {
            if (anEnum.code.equals(code)) {
                return anEnum.msg;
            }
        }
        return code;
    }
}

然后 我们创建一个用户类 在性别和等级上分别加上注解,指定我们注解中获取中文描述的方法,因为我们设置了默认方法,如果方法名和默认方法一致的话就不需要指定了。

/**
 * 功能描述 : 用户类
 *
 * @author ziyear 2021-12-18 19:22
 */
@Data
public class User  {
    private Long id;
    private String name;
    private String mobile;
    @EnumSerialize(SexEnum.class)
    private String sex;
    @EnumSerialize(value = UserLevelEnum.class,method = "getMsg")
    private String level;
}

下边我们搞一个枚举自定义序列化类,对字段上包含 EnumSerialize 的类进行序列化

/**
 * 功能描述 : 枚举自定义序列化
 *
 * @author ziyear 2021-12-18 19:23
 */
@Slf4j
public class EnumsSerializer implements ObjectSerializer {
    private static final String FIELD_KEY_EXT = "Name";
    private static final String BEAN_START = "{";
    private static final String BEAN_END = "}";
    private static final String EXT_FIELD_FORMAT = ",\"{0}\": \"{1}\"";

    @Override
    public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
        if (object == null) {
            serializer.write(object);
            return;
        }
        JavaBeanSerializer javaBeanSerializer = new JavaBeanSerializer(object.getClass()) {
            @Override
            public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
                super.write(serializer, object, fieldName, fieldType, features, true);
            }
        };
        SerializeWriter out = serializer.getWriter();
        out.write(BEAN_START);
        javaBeanSerializer.write(serializer, object, fieldName, fieldType, features);

        try {
            Set<String> fieldNames = javaBeanSerializer.getFieldNames(object);
            for (String name : fieldNames) {
                FieldSerializer fieldSerializer = javaBeanSerializer.getFieldSerializer(name);
                Field field = fieldSerializer.fieldInfo.field;
                if (field != null) {
                    final EnumSerialize annotation = AnnotationUtils.findAnnotation(field, EnumSerialize.class);
                    if (annotation != null) {
                        Class<?> value = annotation.value();
                        String methodName = annotation.method();
                        Method method;
                        try {
                            method = value.getMethod(methodName, String.class);
                        } catch (Exception e) {
                            log.error("方法不存在!{}#{}", value, methodName);
                            continue;
                        }
                        Object fieldValue = javaBeanSerializer.getFieldValue(object, name);
                        Object msg = method.invoke(null, Objects.toString(fieldValue));
                        writeExtField(out, name, msg);
                    }
                }
            }
        } catch (Exception e) {
            log.error("序列化出现错误!", e);
        } finally {
            out.write(BEAN_END);
        }
    }

    private void writeExtField(SerializeWriter out, String key, Object value) {
        String format = MessageFormat.format(EXT_FIELD_FORMAT, key + FIELD_KEY_EXT, value);
        out.write(format);
    }
}

自定义序列化搞好了,然后我们需要将我们的序列化配置到消息转换器中,这样在序列化输出的时候就能用上我们的自定义序列化了

/**
 * 功能描述 : 配置类
 *
 * @author Ziyear 2021-12-18 19:32
 */
@Configuration
public class MvcConfig extends WebMvcConfigurationSupport {
    @Override
    protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        FastJsonHttpMessageConverter fastJsonConverter = new FastJsonHttpMessageConverter();
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(SerializerFeature.QuoteFieldNames,
                SerializerFeature.WriteEnumUsingToString,
                SerializerFeature.WriteMapNullValue,
                SerializerFeature.WriteDateUseDateFormat,
                SerializerFeature.WriteNullStringAsEmpty);
        JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask();
        fastJsonConverter.setSupportedMediaTypes(Lists.newArrayList(MediaType.APPLICATION_JSON_UTF8,
                new MediaType(MediaType.TEXT_HTML, StandardCharsets.UTF_8),
                new MediaType(MediaType.APPLICATION_FORM_URLENCODED, StandardCharsets.UTF_8)));

        // 序列化配置
        SerializeConfig serializeConfig = new SerializeConfig() {
            @Override
            public ObjectSerializer getObjectWriter(Class<?> clazz) {
                // 从类里面寻找注解,如果字段上包含枚举序列化注解的话,我们就走到
                final EnumSerialize annotation = findClassFiledAnnotation(clazz, EnumSerialize.class);
                if (annotation != null) {
                    return new EnumsSerializer();
                }
                return super.getObjectWriter(clazz);
            }
        };
        fastJsonConfig.setSerializeConfig(serializeConfig);
        fastJsonConverter.setFastJsonConfig(fastJsonConfig);
        converters.add(fastJsonConverter);
        super.configureMessageConverters(converters);
    }

    public static <A extends Annotation> A findClassFiledAnnotation(Class<?> clazz, Class<A> annotation) {
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            A result = declaredField.getAnnotation(annotation);
            if (result != null) {
                return result;
            }
        }
        return null;
    }
}

配置完成
我们搞一个接口试试

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @RestController
    static class TestRest {
        @RequestMapping("/test")
        public Object getUser() {
            User user = new User();
            user.setId(1L);
            user.setName("张三");
            user.setMobile("18000000000");
            user.setSex(SexEnum.MAN.getCode());
            user.setLevel(UserLevelEnum.DEFAULT.getCode());
            return user;
        }
    }
}

启动项目,我们访问一下
GET http://localhost:8080/test

{
  "id": 1,
  "level": "default",
  "mobile": "18000000000",
  "name": "张三",
  "sex": "man",
  "levelName": "普通会员",
  "sexName": "男"
}

一般我们返回给前端都会把user进行包装一下,那我们再试一下吧user进行包装一下看看效果吧

/**
 * 功能描述 : 通用返回结果封装
 *
 * @author ziyear 2020-12-18 19:54
 */
@Data
public class ActionResult<T> implements Serializable {

    public static final String SUCCESS_CODE = "000000";
    public static final String ERROR_CODE = "999999";

    /**
     * 操作编码
     */
    private String code;

    /**
     * 返回值
     */
    private T data;

    /**
     * 操作是否成功
     */
    private boolean isSuccess = true;

    /**
     * 操作消息
     */
    private String message;

    public ActionResult(String code, T data, String message) {
        this.isSuccess = false;
        this.code = code;
        this.data = data;
        this.message = message;
    }
    public static <T> ActionResult<T> getSuccessResult(T value) {
        return new ActionResult<>(SUCCESS_CODE, value, "成功");
    }
}

改一下返回值

	@RestController
    static class TestRest {
        @RequestMapping("/test")
        public Object getUser() {
            User user = new User();
            user.setId(1L);
            user.setName("张三");
            user.setMobile("18000000000");
            user.setSex(SexEnum.MAN.getCode());
            user.setLevel(UserLevelEnum.DEFAULT.getCode());
            return ActionResult.getSuccessResult(user);
        }
    }

启动项目,我们再次访问一下
GET http://localhost:8080/test

{
  "code": "000000",
  "data": {
    "id": 1,
    "level": "default",
    "mobile": "18000000000",
    "name": "张三",
    "sex": "man",
    "levelName": "普通会员",
    "sexName": "男"
  },
  "message": "成功",
  "success": false
}

可以看到一样是生效的
ok到此,我们的序列化动态增加字段就讲解完了。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值