论坛首页 Java企业应用论坛

开源一个扩展性强的Json库(jsonconvert-0.1.0beta版)

浏览 8031 次
精华帖 (0) :: 良好帖 (14) :: 新手帖 (0) :: 隐藏帖 (1)
作者 正文
   发表时间:2011-08-14   最后修改:2011-09-01

 

正式版已经发布:  http://www.iteye.com/topic/1114250

 

由于工作上有json格式的数据需要处理, 需要一个json库, 但是网上的json库不是性能不好就是功能有限。 不得已逼自己动手写一个。 由于时间有限, 目前这个库只是用最基础的反射实现。 我打算分三个阶段完善这个jsonconvert库。

       1 json的框架与扩展性设计

       2 反序列化与序列化功能的优化

       3 性能优化,去掉反射改用asm

  虽然目前是用反射实现的, 但是其性能已经不比其他json库差多少。 初期大部分时间花在框架与反序列化的设计上面。  实现细节方面没有下太多功夫。 现在将jsonconvert开源出来, 希望集思广益, 从网友那吸收更多的金点子~  : )

   先让大家看看jsonconvert的性能是个什么程度, 如果性能不好, 相信大家也不会再往下看了。 最近看到iteye上面有个fastjson库, 性能算是目前最快的, 下面是与fastjson简单的性能比较结果: (测试代码见附件)

       --------------------------NodeRecord-----------------------------------

       jsonconvert 循环解析单例10000次耗时: 786

         fastjson  循环解析单例10000次耗时: 1466

       jsonconvert 循环解析数组10000次耗时: 13403

         fastjson  循环解析数组10000次耗时: 27020

       -----------------------------------------------------------------------

       jsonconvert 循环序列单例10000次耗时: 867

         fastjson  循环序列单例10000次耗时: 809

       jsonconvert 循环序列数组10000次耗时: 16270

         fastjson  循环序列数组10000次耗时: 13830

 

       --------------------------SimpleRecord-----------------------------------

       jsonconvert 循环解析单例10000次耗时: 349

         fastjson  循环解析单例10000次耗时: 433

       jsonconvert 循环解析数组10000次耗时: 3234

         fastjson  循环解析数组10000次耗时: 6454

       -------------------------------------------------------------------------

       jsonconvert 循环序列单例10000次耗时: 240

         fastjson  循环序列单例10000次耗时: 327

       jsonconvert 循环序列数组10000次耗时: 3769

         fastjson  循环序列数组10000次耗时: 3051

 

可以看出jsonconvert虽然是反射做的, 其性能已经不比fastjson差了。 而且如果测试的JavaBean内嵌层越多, fastjson的差距就会越大。  本来想用JDK7里面的InvokeDynamic替换反射, 但是考虑兼容JDK56,且以后始终会用asm代替, 所以没有使用, 简单的测试过InvokeDynamic,性能只是比反射快1-2倍而已,与直接调用的差距还是有一段滴。

下面介绍一下jsonconvert的功能。 普通对象只需要使用一个类JsonConvert 该类只有三个方法:

 

 

public static String convertTo(Object value);  
public static <T> T convertFrom(String text, Class<T> clazz);  
public static <T> T[] convertArrayFrom(String text, Class<T> clazz);
 

 

 

    简单介绍下jsonconvert的功能,目前默认支持的类型:

 

              boolean -- double

              Boolean[]-- double[]

              String String[]

              Enum  Enum[]

              Collection

              Map<String, ?>

              树形循环引用(有空会重点介绍)

 

 

    jsonconvert最大的特点是扩展性强。 含扩展类(有时间再写个详细的文档)

       JsonListener     单个字段类(根据一个字符串值就可以转换成的对象)

       JsonObjectToken:  大对象类("{" 开头的对象)

       JsonObjectPrinter  序列化类

      

  任何自定义的扩展都可以通过JsonFactroy 类进行注册:

 

 

public static <E> void register(Class<E> clazz, JsonListener<E> listener);

public static <E> void register(Class<E> clazz, JsonObjectPrinter<E> printer);

public static <E> void register(Class<E> clazz, JsonObjectCloner<? extends E> cloner);

public static <E> void register(Class<E> clazz, Class<? extends JsonObjectToken<E>> token);
 

 

 

    单个字段的简单配置可以通过JsonRef注解进行配置. 

@Inherited

 

 

@Documented
@Target({METHOD})
@Retention(RUNTIME)
public @interface JsonRef {

    //别名
    String alias() default "";

    //屏蔽getter
    boolean ignore() default false;

    /**
     * 只有当参数为Collection/Map才有效
     * @return 
     */
    Class<?> type() default void.class;

    /**
     * 只有当参数为Collection/Map才有效
     * @return 
     */
    Class<?> component() default void.class;
}


    例如:

 

 

class Record {

    private List nodes;

    public List getNodes() {
        return nodes;
    }
    public void setNodes(List nodes) {
        this.nodes = nodes;
    }
}
 

 

 

这个类的nodes字段只是声明为List,解析的时候并不知道list的元素是什么类型, 所以需要显示的指明:

 

 

class Record {

    private List nodes;

    public List getNodes() {
        return nodes;
    }

    @JsonRef(component = String.class)
    public void setNodes(List nodes) {
        this.nodes = nodes;
    }
}
 

 

 

   当然如果直接写明 public void setNodes(List<String> nodes); 就无需JsonRef.

   List默认实例会使用ArrayList 如果你想使用其他List的实现类需要JsonRef

 

 

class Record {

    private List nodes;

    public List getNodes() {
        return nodes;
    }

    @JsonRef(type = LinkedList.class, component = String.class)
    public void setNodes(List nodes) {
        this.nodes = nodes;
    }
}
 

 

 

同理Map的默认实例用HashMap Set的默认实例用HashSet,  QueueDeque的默认实例用LinkedList

 

 

 

   由于时间有限, 下面只简单介绍下JsonListener :

Jsonconvert里面所有基础数据类型都是根据JsonListener 转换的。 大家可以看到很多java库里面都会把booleanintboolean[]int[]String 这些类进行特殊处理。 jsonconvert里面秉着纯面向对象思想, 不会给booleanint这些"异类"数据类型给予国企优待。与其他类型一视同仁。 库默认只实现了8个基本数值类型与String、枚举类。 很多其他常用类( Date, BigInteger, AtomicLong)并没有内置(后期可能会加上)。如果你当前有JavaBean的字段用到了Date类型, 你可以手工给jsonconvert注册DateJsonListener:

import com.tencent.tendon.convert.json.*;

 

 

public class DateJsonListener implements JsonListener<Date> {  
      
	@Override  
	public String convertTo(Date value) {  
	    return value == null ? null : String.valueOf(value.getTime());  
	}  

	@Override  
	public Date convertFrom(String value) {  
	    return value == null ? null : new Date(Long.parseLong(value));  
	}  

	@Override  
	public boolean quotable() {  
	    return true;  
	}  
}  

JsonFactory.register(java.util.Date.class, new DateJsonListener()); 

 

 

同时你可以更改框架里面的默认实现。 比如框架的里面的byte[] 字段的序列化得到的是[1,2,3] 这种类int[]字符串。 如果你想将byte[]转换成base64字符串输出可以给JsonFactory注册自己的byte[]对象的JsonListener:

 

 

 

JsonFactory.register(byte[].class, new JsonListener<byte[]>() {  
  
    @Override  
    public String convertTo(byte[] value) {  
	if (value == null) return null;  
	return JsonWriter.hexTo(value);  
    }  

    @Override  
    public byte[] convertFrom(String value) {  
	if (value == null) return null;  
	return JsonWriter.hexFrom(value);  
    }  

    @Override  
    public boolean quotable() {  
	return true;  
    }  
});
 

 

 

 

循环引用的简单例子:

 

 

public class Person {  
  
    private String chname;  
  
    private Person parent;  
  
    private Person[] children;  
  
    public static void main(String[] args) throws Throwable {  
        Person grandpa = new Person();  
        grandpa.setChname("爷爷");  
  
        Person father = new Person();  
        father.setParent(grandpa);  
        father.setChname("父亲");  
          
        Person uncle = new Person();  
        uncle.setParent(grandpa);  
        uncle.setChname("叔叔");  
                  
        Person son = new Person();  
        son.setParent(father);  
        son.setChname("儿子");  
          
        Person gril = new Person();  
        gril.setParent(uncle);  
        gril.setChname("堂妹");  
          
        grandpa.setChildren(new Person[]{father,uncle});  
        father.setChildren(new Person[]{son});  
        uncle.setChildren(new Person[]{gril});  
          
        String json = JsonConvert.convertTo(grandpa);  
        System.out.println(json);  
        System.out.println(JsonConvert.convertFrom(json, Person.class));   
    }  
      
    /**  -------setter  getter-----**/  
}

输出结果:

{"children":[{"children":[{"chname":"儿子","parent":{"@":"@@@"}}],"chname":"父亲","parent":{"@":"@@@"}},{"children":[{"chname":"堂妹","parent":{"@":"@@@"}}],"chname":"叔叔","parent":{"@":"@@@"}}],"chname":"爷爷"}  
{"children":[{"children":[{"chname":"儿子","parent":{"@":"@@@"}}],"chname":"父亲","parent":{"@":"@@@"}},{"children":[{"chname":"堂妹","parent":{"@":"@@@"}}],"chname":"叔叔","parent":{"@":"@@@"}}],"chname":"爷爷"}
 

 

   {'@':xxx}  就表示引用, {'@':'@'} 表示引用this   {'@':'@@'} 表示引用parent    {'@':'@@@'} 表示parent.parent 依次类推;  之所以不用$这样的字符因为包括java在内很多语言的字段名都允许$字符。 后期可能支持{'@':'@@@.father'}  这种含字段名的格式。

 

 

jsonconvert库下载地址:

   http://tendon.googlecode.com/svn/trunk/tendon.jsonconvert/tags/0.1.0-beta2/

 

(待续)....

 

 

   发表时间:2011-08-16  
给LZ一个建议,弄几张图说明一下你的jsonListener等的一些内容。还有就是你的performance test最好代码放出来,好让大家知道比fastjson快在哪,还是你构造了一个有利于自己的环境而已

看lz的package应该是腾讯的吧,简单的过了下代码,代码还是蛮漂亮的。
1. 缺少单元测试
2. 缺少构建工具(用个maven,ant等,不然你让人家怎么导入eclipse工程)
0 请登录后投票
   发表时间:2011-08-16  
腾讯的和阿里的干上了,呵呵
0 请登录后投票
   发表时间:2011-08-16  
icanfly 写道
腾讯的和阿里的干上了,呵呵

楼上的  不要误会, 纯属技术探讨, 不要扯上公司层面了, 我不是高层, 呵
如果我跟一个美国人争论, 你会不会说中国与美国干上了呢~
0 请登录后投票
   发表时间:2011-08-16  
支持 有竞争才会有提升 最好国内领头的互联网公司 踊跃参与竞争 多搞些开源软件 进入百家争鸣 百花争妍 我们这些Coder就有福啦
0 请登录后投票
   发表时间:2011-08-16  
这个怎么导入?什么构建?
0 请登录后投票
   发表时间:2011-08-16  
nbzhang的代码还是有着当年的风格, 只是比以前要更加精炼,更加全面了。
0 请登录后投票
   发表时间:2011-08-16  
kamhung 写道
icanfly 写道
腾讯的和阿里的干上了,呵呵

楼上的  不要误会, 纯属技术探讨, 不要扯上公司层面了, 我不是高层, 呵
如果我跟一个美国人争论, 你会不会说中国与美国干上了呢~

我就没误会,是你太认真啦,大家都知道这是一个joke
0 请登录后投票
   发表时间:2011-08-16  
jin52yang 写道
这个怎么导入?什么构建?

下载源码自己建个工程就可以了, 现在很多人还是在用Eclipse, 本人用的Netbeans, 所以不提供工程文件, 因为每个人的IDE都不一样。
0 请登录后投票
   发表时间:2011-08-16  
通过github.com/eishay/jvm-serializers/提供的测试程序,比fastjson还是慢不少的。

encode:
alibaba-fastjson	2,262
tencent-convert       2,912


decode:
alibaba-fastjson	1,528
tencent-convert	5,295


0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics