java中常见的json解析方法、库以及性能对比

常见的json解析有原生的JSONObject和JSONArray方法,谷歌的GSON库,阿里的fastjson,还有jackson,json-lib。

  • Gson(项目地址:https://github.com/google/gson)。Gson是目前功能最全的Json解析神器,Gson当初是为因应Google公司内部需求而由Google自行研发而来,但自从在2008年五月公开发布第一版后已被许多公司或用户应用。Gson的应用主要为toJson与fromJson两个转换函数,无依赖,不需要例外额外的jar,能够直接跑在JDK上。而在使用这种对象转换之前需先创建好对象的类型以及其成员才能成功的将JSON字符串成功转换成相对应的对象。类里面只要有get和set方法,Gson完全可以将复杂类型的json到bean或bean到json的转换,是JSON解析的神器。
  • FastJson(项目地址:https://github.com/alibaba/fastjson)。Fastjson是一个Java语言编写的高性能的JSON处理器,由阿里巴巴公司开发。无依赖,不需要例外额外的jar,能够直接跑在JDK上。FastJson在复杂类型的Bean转换Json上会出现一些问题,可能会出现引用的类型,导致Json转换出错,需要制定引用。FastJson采用独创的算法,将parse的速度提升到极致,超过所有json库。
  • Jackson(项目地址:https://github.com/FasterXML/jackson)。相比json-lib框架,Jackson所依赖的jar包较少,简单易用并且性能也要相对高些。而且Jackson社区相对比较活跃,更新速度也比较快。Jackson对于复杂类型的json转换bean会出现问题,一些集合Map,List的转换出现问题。Jackson对于复杂类型的bean转换Json,转换的json格式不是标准的Json格式。
  • Json-lib(项目地址:http://json-lib.sourceforge.net/index.html)。json-lib最开始的也是应用最广泛的json解析工具,json-lib 不好的地方确实是依赖于很多第三方包,包括commons-beanutils.jar,commons-collections-3.2.jar,commons-lang-2.6.jar,commons-logging-1.1.1.jar,ezmorph-1.0.6.jar,对于复杂类型的转换,json-lib对于json转换成bean还有缺陷,比如一个类里面会出现另一个类的list或者map集合,json-lib从json到bean的转换就会出现问题。json-lib在功能和性能上面都不能满足现在互联网化的需求。

    1、java方法解析

  • JSONObject解析json对象
  • JSONArray解析json数组
    例如json字符串为:
{
    "personData": [
        {
            "age": 12,
            "name": "nate",
            "schoolInfo": [
                {
                    "School_name": "清华"
                },
                {
                    "School_name": "北大"
                }
            ],
            "url": "http://pic.yesky.com/uploadImages/2014/345/36/E8C039MU0180.jpg"
        },
        {
            "age": 24,
            "name": "jack",
            ···
        }
    ],
    "result": 1
}

分析这段json数据,

  • 第一层是一个花括号括号,即jsonObect对象,然后这个对象里面有一个personData的JSONArray数组,以及一个result属性
  • 第二层personData的JSONArray数组,它里面除了属性之外,还有一个schoolInfo的JSONArray数组
  • 第三次是schoolInfo的JSONArray数组里面的JSONObject对象

解析:

public class Httpjson extends Thread {
    private String url;
    private Context context;
    private ListView listView;
    private JsonAdapter adapter;
    private Handler handler;

    public Httpjson(String url, ListView listView, JsonAdapter adapter, Handler handler) {
        super();
        this.url = url;
        this.listView = listView;
        this.adapter = adapter;
        this.handler = handler;
    }

    @Override
    public void run() {
        URL httpUrl;
        try {
            httpUrl = new URL(url);
            ···
    }

    /**
     * 从网络中获取JSON字符串,然后解析
     * @param json
     * @return
     */
    private List<Person> jsonParse(String json) {
        try {
            List<Person> personlist = new ArrayList<Person>();
            JSONObject jsonObject = new JSONObject(json);
            int result = jsonObject.getInt("result");
            if (result == 1) {
                JSONArray jsonArray = jsonObject.getJSONArray("personData");
                for (int i = 0; i < jsonArray.length(); i++) {
                    Person person = new Person();
                    JSONObject personData = jsonArray.getJSONObject(i);
                    int age = personData.getInt("age");
                    String url = personData.getString("url");
                    String name = personData.getString("name");
                    ···    
                    JSONArray schoolInfoArray = personData.getJSONArray("schoolInfo");
                    for (int j = 0; j < schoolInfoArray.length(); j++) {
                        JSONObject schoolInfojson = schoolInfoArray.getJSONObject(j);
                        String schoolName = schoolInfojson.getString("School_name");
                        ···
                    }
                    ···
                }
                return personlist;
            } else {
                Toast.makeText(context, "erro", Toast.LENGTH_SHORT).show();
            }
        } catch (JSONException e) {
            e.printStackTrace();
            Log.e("JsonParseActivity", "json解析出现了问题");
        }

        return null;
    }
}

2、gson解析

(1)简介

  GSON是Google提供的用来在Java对象和JSON数据之间进行映射的Java类库。可以将一个Json字符转成一个Java对象,或者将一个Java转化为Json字符串。

(2)特点

  • 快速、高效
  • 代码量少、简洁
  • 面向对象
  • 数据传递和解析方便

(3)编写bean类

要写出能让gson使用的bean类,其中的key也就是名字,如date、safe这些必须一一对应起来。其次就是在bean类中,遇到花括号就写一个class类,遇到方括号就写一个Arraylist数组。这些就是主要规则了!内部的class类写成内部内的形式。

public class GsonParseMoGuBean {
    
    public String data;    
    public String name;
    public String packageName;
    ···
    
    public ArrayList<SafeInfo> safe;
    
    public class SafeInfo {
        public String safeDes;
        ···                
    }
    
    public ArrayList<String> screen;
    
}

(4)将json格式的字符串{}解析为java对象

Java对象

/**
     * //将json格式的字符窜{}转换为java对象
     */
    private void jsonToJavaObjectByGson() {
        
        //1获取或创建json数据
        String json ="{\n" +
        "\t\"id\":2, \"name\":\"大虾\", \n" + 
        "\t\"price\":12.3, \n" +
        "\t\"imagePath\":\"http://192.168.10.165:8080/L05_Server/images/f1.jpg\"\n" + 
        "}\n";
        
        //2解析json数据
        Gson gson =new Gson();
        
        //第一个参数是要解析的数据,第二个参数是解析生成的java对象的类
        ShopInfo shopInfo =  gson.fromJson(json, ShopInfo.class);
        
    }

(5)将json格式的字符串{}解析为java对象的list

private void jsonToJavaListByGson() {
        
        //1获取或创建json数据
         String json = "[\n" + 
        "    {\n" + 
                 "        \"id\": 1,\n" + 
        "        \"imagePath\": \"http://192.168.10.165:8080/f1.jpg\",\n" + 
                 "        \"name\": \"大虾 1\",\n" + 
        "        \"price\": 12.3\n" +             "    },\n" +             "    {\n" +  
                 "        \"id\": 2,\n" + 
        "        \"imagePath\": \"http://192.168.10.165:8080/f2.jpg\",\n" +
        "        \"name\": \"大虾 2\",\n" + 
        "        \"price\": 12.5\n" +             "    }\n" +
        "]";
         
        
        //2解析json数据
         Gson gson =new Gson();      
         
         //List<ShopInfo>:是要返回数据的集合
         List<ShopInfo> shops = gson.fromJson(json,new TypeToken<List<ShopInfo>>(){}.getType());
         
        
        //3显示数据
         tv_gson_orignal.setText(json);
            
        tv_gson_last.setText(shops.toString());
        
    }

(6)将java对象转换为json字符串

private void javaToJSONByGson() {
        
        //1获取或创建java数据
        ShopInfo shopInfo = new ShopInfo(1,"鲍鱼",250.0,"baoyu");
        
        
        //2生成json数据
        Gson gson = new Gson();
        
        String json = gson.toJson(shopInfo);
        
        //3显示数据
         tv_gson_orignal.setText(shopInfo.toString());
            
            tv_gson_last.setText(json);
        
    }

(7)将java对象的list转换为json字符串[]

/**
     * //将java对象的list转换为json字符窜
     */
    private void javaToJSONArrayByGson() {
        
        //1获取或创建java数据
        List<ShopInfo> shops =new ArrayList<ShopInfo>();
        
        ShopInfo baoyu = new ShopInfo(1,"鲍鱼",250.0,"baoyu");
        
        ShopInfo longxia = new ShopInfo(1,"龙虾",250.0,"longxia");
        
        shops.add(baoyu);
        
        shops.add(longxia);
        
        //2生成json数据
        Gson gson = new Gson();
        
        String json = gson.toJson(shops);
        
        
        //3显示数据
         tv_gson_orignal.setText(shops.toString());
            
            tv_gson_last.setText(json);
    }

3、fastjson

(1)简介

  在日常的java项目开发中,JSON的使用越来越频繁,对于Json的处理工具也有很多。接下来就介绍一下阿里开源的一个高性能的JSON框架FastJson,功能完善,完全支持标准JSON库,现在已经越来越受到开发者的青睐。

(2)特点

  Fastjson 是一个 Java 语言编写的高性能功能完善的 JSON 库。它采用一种“假定有序快速匹配”的算法,把JSONParse的性能提升到极致,是目前Java语言中最快的JSON库。

(4)将 json 格式的字符串{}转换为 Java 对象

private void jsonToJavaObjectByFastJson() {

// 1 获取或创建 JSON 数据
 String json = "{\n" +
"\t\"id\":2, \"name\":\"大虾\", \n" +
"\t\"price\":12.3, \n" +
"\t\"imagePath\":\"http://192.168.10.165:8080/L05_Server/images/f1.jpg\ "\n" +
"}\n";

// 2 解析 JSON 数据
ShopInfo shopInfo = JSON.parseObject(json, ShopInfo.class);

}

(4)将 json 格式的字符串[]转换为 Java 对象的 List

private void jsonToJavaListByFastJson() {

// 1 获取或创建 JSON 数据
 String json = "[\n" +
" {\n"+
" \"id\": 1,\n" +
" \"imagePath\":
\"http://192.168.10.165:8080/f1.jpg\",\n" +
" " " " " "
\"name\": \"大虾 1\",\n" +
\"price\": 12.3\n" + },\n" +
{\n"+
\"id\": 2,\n" + \"imagePath\":
\"http://192.168.10.165:8080/f2.jpg\",\n" +
" \"name\": \"大虾 2\",\n" +
" \"price\": 12.5\n" + " }\n"+
"]";

// 2 解析 JSON 数据
List<ShopInfo> shopInfos = JSON.parseArray(json, ShopInfo.class);
}

(5)将 Java 对象转换为 json 字符串

private void javaToJsonObjectByFastJson() {
// 1 获取 Java 对象
ShopInfo shopInfo = new ShopInfo(1, "鲍鱼", 250.0, "baoyu");
// 2 生成 JSON 数据
String json = JSON.toJSONString(shopInfo);
// 3 数据显示 tv_fastjson_orignal.setText(shopInfo.toString()); tv_fastjson_last.setText(json);
}

(7)将 Java 对象的 List 转换为 json 字符串[]

private void javaToJsonArrayByFastJson() {
// 1 获取 Java 集合
List<ShopInfo> shops = new ArrayList<>();
ShopInfo baoyu = new ShopInfo(1, "鲍鱼", 250.0, "baoyu");
ShopInfo longxia = new ShopInfo(2, "龙虾", 251.0, "longxia"); shops.add(baoyu);
shops.add(longxia);
// 2 生成 JSON 数据
String json = JSON.toJSONString(shops);
// 3 数据显示 tv_fastjson_orignal.setText(shops.toString()); tv_fastjson_last.setText(json);
}

4、性能对比

选择一个合适的JSON库要从多个方面进行考虑:

  • 字符串解析成JSON性能
  • 字符串解析成JavaBean性能
  • JavaBean构造JSON性能
  • 集合构造JSON性能
  • 易用性
    编写性能测试
    接下来开始编写这四个库的性能测试代码。

(1)添加maven依赖

当然首先是添加四个库的maven依赖,公平起见,我全部使用它们最新的版本:

<!-- Json libs-->
<dependency>
    <groupId>net.sf.json-lib</groupId>
    <artifactId>json-lib</artifactId>
    <version>2.4</version>
    <classifier>jdk15</classifier>
</dependency>
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.2</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.46</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.4</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.9.4</version>
</dependency>

(2)四个库的工具类

java FastJsonUtil.java

public class FastJsonUtil { public static String bean2Json(Object obj) { return JSON.toJSONString(obj); }

public static <T> T json2Bean(String jsonStr, Class<T> objClass) {
    return JSON.parseObject(jsonStr, objClass);
}
}

java GsonUtil.java

public class GsonUtil {
    private static Gson gson = new GsonBuilder().create();

    public static String bean2Json(Object obj) {
        return gson.toJson(obj);
    }

    public static <T> T json2Bean(String jsonStr, Class<T> objClass) {
        return gson.fromJson(jsonStr, objClass);
    }

    public static String jsonFormatter(String uglyJsonStr) {
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        JsonParser jp = new JsonParser();
        JsonElement je = jp.parse(uglyJsonStr);
        return gson.toJson(je);
    }
}

java JacksonUtil.java

public class JacksonUtil { private static ObjectMapper mapper = new ObjectMapper();

public static String bean2Json(Object obj) {
    try {
        return mapper.writeValueAsString(obj);
    } catch (JsonProcessingException e) {
        e.printStackTrace();
        return null;
    }
}

public static <T> T json2Bean(String jsonStr, Class<T> objClass) {
    try {
        return mapper.readValue(jsonStr, objClass);
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}
}

java JsonLibUtil.java

public class JsonLibUtil {

    public static String bean2Json(Object obj) {
        JSONObject jsonObject = JSONObject.fromObject(obj);
        return jsonObject.toString();
    }

    @SuppressWarnings("unchecked")
    public static <T> T json2Bean(String jsonStr, Class<T> objClass) {
        return (T) JSONObject.toBean(JSONObject.fromObject(jsonStr), objClass);
    }
}

(3)准备Model类

这里我写一个简单的Person类,同时属性有Date、List、Map和自定义的类FullName,最大程度模拟真实场景。

public class Person {
    private String name;
    private FullName fullName;
    private int age;
    private Date birthday;
    private List<String> hobbies;
    private Map<String, String> clothes;
    private List<Person> friends;

    // getter/setter省略

    @Override
    public String toString() {
        StringBuilder str = new StringBuilder("Person [name=" + name + ", fullName=" + fullName + ", age="
                + age + ", birthday=" + birthday + ", hobbies=" + hobbies
                + ", clothes=" + clothes + "]\n");
        if (friends != null) {
            str.append("Friends:\n");
            for (Person f : friends) {
                str.append("\t").append(f);
            }
        }
        return str.toString();
    }

}
public class FullName {
    private String firstName;
    private String middleName;
    private String lastName;

    public FullName() {
    }

    public FullName(String firstName, String middleName, String lastName) {
        this.firstName = firstName;
        this.middleName = middleName;
        this.lastName = lastName;
    }

    // 省略getter和setter

    @Override
    public String toString() {
        return "[firstName=" + firstName + ", middleName="
                + middleName + ", lastName=" + lastName + "]";
    }
}

(4)JSON序列化性能基准测试

@BenchmarkMode(Mode.SingleShotTime)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Benchmark)
public class JsonSerializeBenchmark {
    /**
     * 序列化次数参数
     */
    @Param({"1000", "10000", "100000"})
    private int count;

    private Person p;

    public static void main(String[] args) throws Exception {
        Options opt = new OptionsBuilder()
                .include(JsonSerializeBenchmark.class.getSimpleName())
                .forks(1)
                .warmupIterations(0)
                .build();
        Collection<RunResult> results =  new Runner(opt).run();
        ResultExporter.exportResult("JSON序列化性能", results, "count", "秒");
    }

    @Benchmark
    public void JsonLib() {
        for (int i = 0; i < count; i++) {
            JsonLibUtil.bean2Json(p);
        }
    }

    @Benchmark
    public void Gson() {
        for (int i = 0; i < count; i++) {
            GsonUtil.bean2Json(p);
        }
    }

    @Benchmark
    public void FastJson() {
        for (int i = 0; i < count; i++) {
            FastJsonUtil.bean2Json(p);
        }
    }

    @Benchmark
    public void Jackson() {
        for (int i = 0; i < count; i++) {
            JacksonUtil.bean2Json(p);
        }
    }

    @Setup
    public void prepare() {
        List<Person> friends=new ArrayList<Person>();
        friends.add(createAPerson("小明",null));
        friends.add(createAPerson("Tony",null));
        friends.add(createAPerson("陈小二",null));
        p=createAPerson("邵同学",friends);
    }

    @TearDown
    public void shutdown() {
    }

    private Person createAPerson(String name,List<Person> friends) {
        Person newPerson=new Person();
        newPerson.setName(name);
        newPerson.setFullName(new FullName("zjj_first", "zjj_middle", "zjj_last"));
        newPerson.setAge(24);
        List<String> hobbies=new ArrayList<String>();
        hobbies.add("篮球");
        hobbies.add("游泳");
        hobbies.add("coding");
        newPerson.setHobbies(hobbies);
        Map<String,String> clothes=new HashMap<String, String>();
        clothes.put("coat", "Nike");
        clothes.put("trousers", "adidas");
        clothes.put("shoes", "安踏");
        newPerson.setClothes(clothes);
        newPerson.setFriends(friends);
        return newPerson;
    }
}

说明一下,上面的代码中

ResultExporter.exportResult("JSON序列化性能", results, "count", "秒");

这个是我自己编写的将性能测试报告数据填充至Echarts图,然后导出png图片的方法,具体代码我就不贴了,参考我的github源码。

执行后的结果图:

从上面的测试结果可以看出,序列化次数比较小的时候,Gson性能最好,当不断增加的时候到了100000,Gson明细弱于Jackson和FastJson, 这时候FastJson性能是真的牛,另外还可以看到不管数量少还是多,Jackson一直表现优异。而那个Json-lib简直就是来搞笑的。^_^

(5)JSON反序列化性能基准测试

@BenchmarkMode(Mode.SingleShotTime)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Benchmark)
public class JsonDeserializeBenchmark {
    /**
     * 反序列化次数参数
     */
    @Param({"1000", "10000", "100000"})
    private int count;

    private String jsonStr;

    public static void main(String[] args) throws Exception {
        Options opt = new OptionsBuilder()
                .include(JsonDeserializeBenchmark.class.getSimpleName())
                .forks(1)
                .warmupIterations(0)
                .build();
        Collection<RunResult> results =  new Runner(opt).run();
        ResultExporter.exportResult("JSON反序列化性能", results, "count", "秒");
    }

    @Benchmark
    public void JsonLib() {
        for (int i = 0; i < count; i++) {
            JsonLibUtil.json2Bean(jsonStr, Person.class);
        }
    }

    @Benchmark
    public void Gson() {
        for (int i = 0; i < count; i++) {
            GsonUtil.json2Bean(jsonStr, Person.class);
        }
    }

    @Benchmark
    public void FastJson() {
        for (int i = 0; i < count; i++) {
            FastJsonUtil.json2Bean(jsonStr, Person.class);
        }
    }

    @Benchmark
    public void Jackson() {
        for (int i = 0; i < count; i++) {
            JacksonUtil.json2Bean(jsonStr, Person.class);
        }
    }

    @Setup
    public void prepare() {
        jsonStr="{\"name\":\"邵同学\",\"fullName\":{\"firstName\":\"zjj_first\",\"middleName\":\"zjj_middle\",\"lastName\":\"zjj_last\"},\"age\":24,\"birthday\":null,\"hobbies\":[\"篮球\",\"游泳\",\"coding\"],\"clothes\":{\"shoes\":\"安踏\",\"trousers\":\"adidas\",\"coat\":\"Nike\"},\"friends\":[{\"name\":\"小明\",\"fullName\":{\"firstName\":\"xxx_first\",\"middleName\":\"xxx_middle\",\"lastName\":\"xxx_last\"},\"age\":24,\"birthday\":null,\"hobbies\":[\"篮球\",\"游泳\",\"coding\"],\"clothes\":{\"shoes\":\"安踏\",\"trousers\":\"adidas\",\"coat\":\"Nike\"},\"friends\":null},{\"name\":\"Tony\",\"fullName\":{\"firstName\":\"xxx_first\",\"middleName\":\"xxx_middle\",\"lastName\":\"xxx_last\"},\"age\":24,\"birthday\":null,\"hobbies\":[\"篮球\",\"游泳\",\"coding\"],\"clothes\":{\"shoes\":\"安踏\",\"trousers\":\"adidas\",\"coat\":\"Nike\"},\"friends\":null},{\"name\":\"陈小二\",\"fullName\":{\"firstName\":\"xxx_first\",\"middleName\":\"xxx_middle\",\"lastName\":\"xxx_last\"},\"age\":24,\"birthday\":null,\"hobbies\":[\"篮球\",\"游泳\",\"coding\"],\"clothes\":{\"shoes\":\"安踏\",\"trousers\":\"adidas\",\"coat\":\"Nike\"},\"friends\":null}]}";
    }

    @TearDown
    public void shutdown() {
    }
}

执行后的结果图:

从上面的测试结果可以看出,反序列化的时候,Gson、Jackson和FastJson区别不大,性能都很优异,而那个Json-lib还是来继续搞笑的。

转载声明:性能比较部分由客由熊能创作 © 飞污熊博客。

猜你喜欢

转载自www.cnblogs.com/zyh0430/p/12009310.html