Gson切面实践

背景
  应用切面做日志记录,记录操作实体详情时可以使用JSON格式,这里是我使用Gson包做切面日志的一些实践,总结了遇到的问题。


软件包和学习方法
  这里使用google的Gson包做JSON转换, 这里是项目的地址,可以查看上面的API和User Guide。务必将源码和API配置好,文档中的记录不是很完善,很多时候需要查看源码。
  注意,我使用的是2.2版,因为较早的1.4版本的FieldAttributes类中没有getDeclaringClass()这个方法,这个方法是获取field所属的类,在我的排除策略中会用到。
 

排除策略
  最简单的gson转换可以是这样的,但却没有多少实际的作用。切面日志时,一个实体和其他实体存在关联,这时候就需要通过自定义排除策略决定如何转换关联对象,否则可能出现“爆炸式”的json字符串。
Java代码   收藏代码
  1. Gson gson = new Gson();  
  2. int[] ints = {12345};  
  3. String[] strings = {"abc""def""ghi"};  
  4.   
  5. // Serialization  
  6. gson.toJson(ints);     ==> prints [1,2,3,4,5]  
  7. gson.toJson(strings);  ==> prints ["abc""def""ghi"]  

  下面是我定义的一个排除策略的类,能基本满足需求,从内网搬过来的,未测试
Java代码   收藏代码
  1. package com.lingceng.magic.logutil;  
  2.   
  3. import org.apache.commons.lang.ArrayUtils;  
  4. import org.slf4j.Logger;  
  5. import org.slf4j.LoggerFactory;  
  6.   
  7. import com.google.gson.ExclusionStrategy;  
  8. import com.google.gson.FieldAttributes;  
  9.   
  10. public class TargetStrategy implements ExclusionStrategy {  
  11.     private static Logger log = LoggerFactory.getLogger(TargetStrategy.class);  
  12.     private Class<?> target;  
  13.     private String[] fields;  
  14.     private Class<?>[] clazz;  
  15.     private boolean reverse;  
  16.   
  17.     public TargetStrategy(Class<?> target) {  
  18.         super();  
  19.         this.target = target;  
  20.     }  
  21.   
  22.     @Override  
  23.     public boolean shouldSkipClass(Class<?> class1) {  
  24.         return false;  
  25.     }  
  26.   
  27.     @Override  
  28.     public boolean shouldSkipField(FieldAttributes fieldattributes) {  
  29.         Class<?> owner = fieldattributes.getDeclaringClass();  
  30.         Class<?> c = fieldattributes.getDeclaredClass();  
  31.         String f = fieldattributes.getName();  
  32.         boolean isSkip = false;  
  33.           
  34.         if (owner == target) {  
  35.             if (ArrayUtils.contains(fields, f)) {  
  36.                 log.debug("fitler field:{} for class:{}", f, owner);  
  37.                 isSkip = true;  
  38.             }  
  39.             if (ArrayUtils.contains(clazz, c)) {  
  40.                 log.debug("fitler class:{} for class:{}", c, owner);  
  41.                 isSkip = true;  
  42.             }  
  43.             if (reverse) {  
  44.                 isSkip = !isSkip;  
  45.             }  
  46.         }  
  47.   
  48.         return isSkip;  
  49.     }  
  50.   
  51.     public void setFields(String[] fields) {  
  52.         this.fields = fields;  
  53.     }  
  54.   
  55.     public void setClazz(Class<?>[] clazz) {  
  56.         this.clazz = clazz;  
  57.     }  
  58.   
  59.     public void setReverse(boolean reverse) {  
  60.         this.reverse = reverse;  
  61.     }  
  62. }  

使用的时候是这样的
Java代码   收藏代码
  1. TargetStrategy ts = new TargetStrategy(Student.class);  
  2. //这里表示仅转换Student中的id和name属性  
  3. ts.setFields(new String[] {"id""name"});  
  4. ts.setReverse(true);  
  5.   
  6. Gson gson = new GsonBuilder().setExcludeStrategy(ts).create();  
  7. gson.toJson(teacher);  


HibernateProxy异常处理
  在使用Hibernate时,那么很可能遇到 这样的错误
java.lang.UnsupportedOperationException: Attempted to serialize java.lang.Class: org.hibernate.proxy.HibernateProxy. Forgot to register a type adapter?
  因为gson在转换时是使用的反射机制,当获取的实体对象还在hibernate代理的时候,例如刚通过Id获取到,这时候获取到的便是代理对象HibernateProxy。这和直接调用实体对象的get方法不同,获取对象的属性就不能起作用。
  解决的方法便是将代理对象实例化,见下面的代码
Java代码   收藏代码
  1. /** 
  2.  * This TypeAdapter unproxies Hibernate proxied objects, and serializes them 
  3.  * through the registered (or default) TypeAdapter of the base class. 
  4.  */  
  5. public class HibernateProxyTypeAdapter extends TypeAdapter<HibernateProxy> {  
  6.   
  7.     public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {  
  8.         @Override  
  9.         @SuppressWarnings("unchecked")  
  10.         public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {  
  11.             return (HibernateProxy.class.isAssignableFrom(type.getRawType()) ? (TypeAdapter<T>) new HibernateProxyTypeAdapter(gson) : null);  
  12.         }  
  13.     };  
  14.     private final Gson context;  
  15.   
  16.     private HibernateProxyTypeAdapter(Gson context) {  
  17.         this.context = context;  
  18.     }  
  19.   
  20.     @Override  
  21.     public HibernateProxy read(JsonReader in) throws IOException {  
  22.         throw new UnsupportedOperationException("Not supported");  
  23.     }  
  24.   
  25.     @SuppressWarnings({"rawtypes""unchecked"})  
  26.     @Override  
  27.     public void write(JsonWriter out, HibernateProxy value) throws IOException {  
  28.         if (value == null) {  
  29.             out.nullValue();  
  30.             return;  
  31.         }  
  32.         // Retrieve the original (not proxy) class  
  33.         Class<?> baseType = Hibernate.getClass(value);  
  34.         // Get the TypeAdapter of the original class, to delegate the serialization  
  35.         TypeAdapter delegate = context.getAdapter(TypeToken.get(baseType));  
  36.         // Get a filled instance of the original class  
  37.         Object unproxiedValue = ((HibernateProxy) value).getHibernateLazyInitializer()  
  38.                 .getImplementation();  
  39.         // Serialize the value  
  40.         delegate.write(out, unproxiedValue);  
  41.     }  
  42. }  

使用的时候将该TypeAdapter的Factory注册到GsonBuilder,上面的代码变为
Java代码   收藏代码
  1. Gson gson = new GsonBuilder().setExcludeStrategy(ts)  
  2. .registerTypeAdapterFactory(HibernateProxyTypeAdapter.FACTORY)  
  3. .create();  
  4. gson.toJson(teacher);  



Javascript格式化
  默认情况下,在页面上显示json字符串时是连在一起的,格式化一下可以便于阅读:
Javascript代码   收藏代码
  1. // get text for textarea, do not use text()  
  2. var msg= $("#jsonText").val();  
  3. // parse json  
  4. msg = JSON.parse(msg);  
  5. // return format string  
  6. msg = JSON.stringify(msg, null, 4);  

这里要注意的是IE8在转换的时候会出现问题,所以需要引入 json.js,将里面的JSON换位JSON2来使用,这时候代码便是这样的
Javascript代码   收藏代码
  1. // get text for textarea, do not use text()  
  2. var msg= $("#jsonText").val();  
  3. // parse json  
  4. msg = JSON2.parse(msg);  
  5. // return format string  
  6. msg = JSON2.stringify(msg, null, 4); 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值