引言.最近对接支付宝支付时,发现支付宝接口返回一个泛型的对象.如{"XXX_YYY_response":{},"sign":"AFDSS#QR@43211423142341FAF"},其中 XXX_YYY_response 是动态变化的,即多个值映射到泛型字段.
GSON 提供了 @SerializeName注解来实现.
1 @Retention(RetentionPolicy.RUNTIME) 2 @Target({ElementType.FIELD, ElementType.METHOD}) 3 public @interface SerializedName { 4 5 /** 6 * @return the desired name of the field when it is serialized or deserialized 7 */ 8 String value(); 9 /** 10 * @return the alternative names of the field when it is deserialized 11 */ 12 String[] alternate() default {}; 13 }
可以给 alternate 赋一个数组来实现.但是每次引入新的支付宝接口时,要变更注解字段 alternate (不能使用变量,因注解字段值必须是常量)的值.不符合开闭原则.
使用反射来解决.给泛型对象添加静态注册名字方法,动态改变注解alternate 的值.在具体对象反序化前可以注册一次.
1 public class AlipayResult<OutParameter extends AlipayBasicOutParameter> { 2 3 private static final Logger LOG= LoggerFactory.getLogger(AlipayResult.class); 4 5 @Expose 6 private String sign; 7 private static final String SIGN_TITLE_TAG="sign"; 8 9 @Expose 10 @SerializedName(value="",alternate = {}) 11 private OutParameter outParameter; 12 13 14 @Expose 15 @SerializedName("error_response") 16 private AlipayError errorObject; 17 18 19 private static final Set<String> JSON_NAMES= new HashSet<>(); 20 21 static { 22 JSON_NAMES.add("error_response"); 23 } 24 25 26 27 public static synchronized final void registerOutParameterAlternate(final String namespace){ 28 try { 29 JSON_NAMES.add(namespace); 30 Field field = AlipayResult.class.getDeclaredField("outParameter"); 31 field.setAccessible(true); 32 SerializedName serialName = field.getDeclaredAnnotation(SerializedName.class); 33 String[] ns = serialName.alternate(); 34 for (String item : ns) { 35 if (item.equals(namespace)) { 36 return; 37 } 38 } 39 InvocationHandler h = Proxy.getInvocationHandler(serialName); 40 // 获取 AnnotationInvocationHandler 的 memberValues 字段 41 Field hField = h.getClass().getDeclaredField("memberValues"); 42 // 因为这个字段事 private final 修饰,所以要打开权限 43 hField.setAccessible(true); 44 // 获取 memberValues 45 Map memberValues = (Map) hField.get(h); 46 // 修改 value 属性值 47 List<String> target =new ArrayList<String>(); 48 if(null!=ns&&ns.length>0){ 49 for (String i:ns){ 50 target.add(i); 51 } 52 } 53 target.add(namespace); 54 String[] alternateArr = target.toArray(new String[0]); 55 memberValues.put("alternate", alternateArr); 56 if (LOG.isInfoEnabled()) { 57 LOG.info("alternate value is " + alternateArr); 58 } 59 }catch (Throwable throwable){ 60 throw new RuntimeException(throwable); 61 } 62 } 63 64 private static final String AlipayResponseNameSpaces=""; 65 66 public String getSign() { 67 return sign; 68 } 69 70 public void setSign(String sign) { 71 this.sign = sign; 72 } 73 74 public OutParameter getOutParameter() { 75 return outParameter; 76 } 77 78 public void setOutParameter(OutParameter outParameter) { 79 this.outParameter = outParameter; 80 }