java枚举与前端的交互

java后台与前端交互时,如果java参数属性是枚举类型,则交互时需要进行序列化与反序列化

当需要把枚举格式以"enum": {"code": "","message": ""}格式返回给前端时,需要使用到@JsonSerializer类进行序列化

Base枚举(所有枚举实现这个Base枚举,在做序列化处理时能够较好的进行枚举的纺一处理)

public interface BaseEnum<E extends Enum<?>, T> {

    //值,数据库中,以及程序中一般传递的都是这个值
    public T getCode();

    public String getMessage();
}
需要返回给前端的枚举,加上@JsonSerialize(using = JsonEnumSerializer.class)注解,JsonEnumSerializer是自定义序列化类
@JsonSerialize(using = JsonEnumSerializer.class)
public enum TestEnum implements BaseEnum<TestEnum,Integer> {
    T1(1,"test1");
    private Integer code;
    private String message;

    TestEnum(int code, String message) {
        this.code = code;
        this.message = message;
    }

    @Override
    public Integer getCode() {
        return code;
    }

    @Override
    public String getMessage() {
        return message;
    }
}

自定义枚举序列化规则,继承JsonSerializer类并重写serialize方法

import com.example.chaosdemo.enums.BaseEnum;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.google.common.base.Throwables;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 返回   "enum": {"code": "2","message": "tom2"} 格式给前端
 */

public class JsonEnumSerializer extends JsonSerializer<BaseEnum> {
    @Override
    public void serialize(BaseEnum value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
        try {
            //类的全限定名
            String name = value.getClass().getName();
            //类的code值
            Object code = value.getCode();

            String message = value.getMessage();

            //这里也不用反射了,性能不好,所以直接new一个map再做序列化
            Map<String, Object> jsonMap = new HashMap<>();
            jsonMap.put("code", code);
            jsonMap.put("message", message);

            serializers.defaultSerializeValue(jsonMap, gen);
        } catch (Exception e) {
            //  logger.error("JsonEnumSerializer serialize error: " + Throwables.getStackTraceAsString(e));
            System.out.println("JsonEnumSerializer serialize error: " + Throwables.getStackTraceAsString(e));
            throw new RuntimeException(e);
        }
    }
}

测试:

controller控制层

@Slf4j
@RestController
public class EnumTestController {
    @RequestMapping(value = "/enum")
    public TestDTO enumTest(@RequestBody TestDTO testDTO) throws JsonProcessingException {
        log.info("enum");
        testDTO.setTestEnum(TestEnum.T1);
        return testDTO;

    }
}

返回的DTO

public class TestDTO {

    private TestEnum testEnum;
    
    public TestEnum getTestEnum() {
        return testEnum;
    }

    public void setTestEnum(TestEnum testEnum) {
        this.testEnum = testEnum;
    }

}

前端接收到的数据

当前端传参数到后端,后端的属性参数是枚举类型时,需要做反序列化,前端只要传对应的code值,后端就能自动传成对应的枚举值

用到3个类

1.自定义反序列化类

import com.example.chaosdemo.enums.BaseEnum;
import com.example.chaosdemo.utils.ReflectionUtils;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonStreamContext;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.google.common.base.Throwables;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.lang.reflect.Field;

/**
 * restful接口中,自定义获取枚举规则的方式。枚举类上加上@JsonDeserialize(using = JsonEnumDeserializer.class) 前端只要传code的值过来就能转成枚举接收
 */
public class JsonEnumDeserializer extends JsonDeserializer<BaseEnum> {
    
    @Override
    public BaseEnum deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {

        try {
            //前端输入的值
            String inputParameter = p.getText();
            if (StringUtils.isBlank(inputParameter)) {
                return null;
            }
            JsonStreamContext parsingContext = p.getParsingContext();
            String currentName = parsingContext.getCurrentName();//字段名
            Object currentValue = parsingContext.getCurrentValue();//前端注入的对象(ResDTO)
            Field field = ReflectionUtils.getField(currentValue.getClass(), currentName); // 通过对象和属性名获取属性的类型
            Class enumClass = field.getType();

            return DefaultInputJsonToEnum.getEnum(inputParameter, enumClass);
        } catch (Exception e) {
           // logger.error("JsonEnumDeserializer deserialize error: " + Throwables.getStackTraceAsString(e));
            System.out.println("JsonEnumDeserializer deserialize error: " + Throwables.getStackTraceAsString(e));
            throw new RuntimeException(e);
        }
    }
}

2.转成对应的枚举的类

import com.example.chaosdemo.enums.BaseEnum;
import com.google.common.base.Throwables;

import java.lang.reflect.Method;

/**
 * 传入的参数中,去相应的枚举code中获取
 */
public class DefaultInputJsonToEnum{

  //  private static final Logger logger = LoggerFactory.getFrameworkLogger(DefaultInputJsonToEnum.class);

    public static BaseEnum getEnum(String inputParameter, Class enumClass) {
        try {
            Method valuesMethod = enumClass.getDeclaredMethod("values");

            BaseEnum[] values = (BaseEnum[]) valuesMethod.invoke(null);
            BaseEnum baseEnum = null;

            for (BaseEnum value : values) {
                //因为inputParameter都是string类型的,code + "" 转成字符串才能比较
                    if (inputParameter.equals(value.getCode() + "")) {
                        baseEnum = value;
                        break;
                    } else {
                        continue;
                    }
                }

            //如果都拿不到,那就直接抛出异常了
            if (baseEnum == null) {
                throw new RuntimeException("输入参数不符合预期");
            }
            return baseEnum;
        } catch (Exception e) {
           // logger.error("getEnum error: " + Throwables.getStackTraceAsString(e));
            throw new RuntimeException(e);
        }
    }

    private BaseEnum getEnumByName(String inputParameter, Class enumClass) {
        try {
            Method valueOfMethod = enumClass.getDeclaredMethod("valueOf", String.class);
            return (BaseEnum) valueOfMethod.invoke(null, inputParameter);
        } catch (Exception e) {
            //这里异常无需抛出,因为这个枚举根据name获取不到就直接抛出异常了...
            //logger.warn("未获取枚举的name值,入参: " + inputParameter + "," + "class : " + enumClass);
            return null;
        }
    }

}

3.反射工具

import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.util.proxy.ProxyObject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.framework.Advised;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 反射工具
 * 
 * @author Chenzx
 *
 */
public class ReflectionUtils {
   
   public static ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();

   // javassist.ClassPool   dubbo中包含此包
   private static ClassPool classPool = ClassPool.getDefault();

   public static List<String> getAllFields(Class<?> clazz) {
      if (clazz == Object.class || clazz.isPrimitive())
         return Collections.emptyList();
      try {
         classPool.insertClassPath(new ClassClassPath(clazz));
         CtClass cc = classPool.get(clazz.getName());
         List<CtClass> ctClasses = new ArrayList<CtClass>();
         ctClasses.add(cc);
         while (!(cc = cc.getSuperclass()).getName().equals(Object.class.getName()))
            ctClasses.add(0, cc);
         List<String> fields = new ArrayList<String>();
         for (CtClass ctc : ctClasses) {
            for (CtField cf : ctc.getDeclaredFields()) {
               int accessFlag = cf.getModifiers();
               if (Modifier.isFinal(accessFlag) || Modifier.isStatic(accessFlag))
                  continue;
               fields.add(cf.getName());
            }
         }
         return fields;
      } catch (Exception e) {
         e.printStackTrace();
         return Collections.emptyList();
      }
   }

   public static Class<?> getGenericClass(Class<?> clazz) {
      return getGenericClass(clazz, 0);
   }

   public static Class<?> getGenericClass(Class<?> clazz, int index) {
      return getGenericClass(clazz.getGenericSuperclass(), index);
   }

   public static Class<?> getGenericClass(Type genType, int index) {
      if (genType instanceof ParameterizedType) {
         ParameterizedType pramType = (ParameterizedType) genType;
         Type[] params = pramType.getActualTypeArguments();
         if ((params != null) && (params.length > index))
            return params[index] instanceof Class ? (Class<?>) params[index] : null;
      }
      return null;
   }

   public static Class<?> getActualClass(Object object) {
      Class<?> c = object.getClass();
      if (object instanceof ProxyObject)
         c = c.getSuperclass();   
      return c;
   }

   public static String[] getParameterNames(Constructor<?> ctor) {
      return getParameterNames(null, ctor);
   }

   public static String[] getParameterNames(Method method) {
      return getParameterNames(method, null);
   }

   private static String[] getParameterNames(Method method, Constructor<?> ctor) {
      Annotation[][] annotations = method != null ? method.getParameterAnnotations() : ctor.getParameterAnnotations();
      String[] names = new String[annotations.length];
      /*
       * boolean allbind = true; loop: for (int i = 0; i < annotations.length;
       * i++) { Annotation[] arr = annotations[i]; for (Annotation a : arr) {
       * if (a instanceof Param) { String s = ((Param) a).value(); if
       * (StringUtils.isNotBlank(s)) { names[i] = s; continue loop; } } }
       * allbind = false; } if (!allbind) {
       */
      String[] namesDiscovered = method != null ? parameterNameDiscoverer.getParameterNames(method)
            : parameterNameDiscoverer.getParameterNames(ctor);
      if (namesDiscovered == null)
         return null;
      for (int i = 0; i < names.length; i++)
         if (names[i] == null)
            names[i] = namesDiscovered[i];
      // }
      return names;
   }

   public static String[] getParameterNames(JoinPoint jp) {
      if (!jp.getKind().equals(JoinPoint.METHOD_EXECUTION))
         return null;
      Class<?> clz = jp.getTarget().getClass();
      MethodSignature sig = (MethodSignature) jp.getSignature();
      Method method;
      try {
         method = clz.getDeclaredMethod(sig.getName(), sig.getParameterTypes());
         if (method.isBridge())
            method = BridgeMethodResolver.findBridgedMethod(method);
         return getParameterNames(method);
      } catch (Exception e) {
         return null;
      }
   }

   public static Field getField(Class<?> clazz, String name) throws NoSuchFieldException {
      try {
         Field f = clazz.getDeclaredField(name);
         f.setAccessible(true);
         return f;
      } catch (NoSuchFieldException e) {
         if (clazz == Object.class)
            throw e;
         return getField(clazz.getSuperclass(), name);
      }

   }

   @SuppressWarnings("unchecked")
   public static <T> T getFieldValue(Object o, String name) {
      try {
         Field f = getField(o.getClass(), name);
         return (T) f.get(o);
      } catch (Exception e) {
         throw new RuntimeException(e.getMessage(), e);
      }
   }

   public static void setFieldValue(Object o, String name, Object value) {
      try {
         Field f = getField(o.getClass(), name);
         f.set(o, value);
      } catch (Exception e) {
         throw new RuntimeException(e.getMessage(), e);
      }
   }

   public static Object getTargetObject(Object proxy) {
      while (proxy instanceof Advised) {
         try {
            return getTargetObject(((Advised) proxy).getTargetSource().getTarget());
         } catch (Exception e) {
            e.printStackTrace();
            return proxy;
         }
      }
      return proxy;
   }

   public static String getCurrentMethodName() {
      return Thread.currentThread().getStackTrace()[2].getMethodName();
   }

}

在需要用到反序列化的枚举类上加上注解就行了

@JsonDeserialize(using = JsonEnumDeserializer.class)

注意:枚举类上使用了序列化和反序列化的,不能使用JSON工具类来转成进行JAVA类和JSON的相互转换。如果需要进行JSON转换的类,不要使用@JsonSerialize和@JsonDeserialize注解

之后发现序列化(返回枚举给前端)可以直接使用

@JsonFormat(shape = JsonFormat.Shape.OBJECT)

效果与上面的自定义序列化一样,如果没有特殊要求可以直接使用此注解,就不用写自定义序列化规则了

猜你喜欢

转载自blog.csdn.net/YZX2018/article/details/86674617
今日推荐