zoukankan      html  css  js  c++  java
  • Json字符串解析原理、超大json对象的解析

     

     

    概述

    附上完整的代码:

    https://files.cnblogs.com/files/xcr1234/json.rar

    一个类实现json解析核心代码(ObjectParser),其他的类都是工具类
    (入口类是Json)



    JSON:JavaScript 对象表示法(JavaScript Object Notation)。

    JSON 是存储和交换文本信息的语法。类似 XML。

    JSON 比 XML 更小、更快,更易解析。

    在JSON中,分为6种对象:

    • 数字(整数或浮点数)
    • 字符串(在双引号中)
    • 逻辑值(true 或 false)
    • 数组(JsonArray)
    • 对象(JsonObject)
    • null

    基本对象的实现

    JsonObject其实就是一个HashMap,JsonArray其实就是一个ArrayList.

    public class JsonObject extends HashMap<String,Object> {
    
    }
    
    public class JsonArray extends ArrayList<Object> {
    
    }

    JSON字符串的解析

    以这个字符串为例:

    {“success”:true,”id”:-10.5,”employees”:[{“firstName”:”Bill”,”lastName”:”Gates”},{“firstName”:”George”,”lastName”:”Bush”},{“firstName”:”Thomas”,”lastName”:”Carter”}]}

    我们保证在只扫描一次整个的情况下,就将json结构解析成功。

    传统的解析策略通常是通过词法分析,将json分为一个个的token,而这些token有着自己的类型和值;再通过语法分析构建一棵抽象语法树,进一步处理。比如""是一种,true/false又是一种。

    其实根本不需要这么复杂。依我看来,json的token只有五种:true/false/null(归为一种,因为它们是固定值)、number、string、object、array。也不用特别在意start和end的Token区分,比如 { 符号和 } 符号。从一个 { 符号开始,到下一个它对应的 } 符号都是属于同一个json object的。这里的 { 与 } 、[ 与 ] 符号都是一一对应的。
    我设计了一个nextObject()方法,它可以解析出json字符串中的下一个对象,然后在适当的时候装配即可。


    nextObject方法的实现

    提取字符

        public static boolean isSpace(char c){
            return c == ' ' || c == '
    ' || c == '
    ';
        }
    
        //方法得到当前字符,忽略空格、换行符
        private char getChar(){
            char c = json.charAt(pos);
            while(isSpace(c)){
                pos++;
                c = getCurrentChar();
            }
            return c;
        }

    上面方法是消耗掉所有空白字符,直到读取到一个非空白字符,isSpace方法用于判断一个字符是否属于空白字符,pos表示当前指针指向的那个字符。也就是说,DFA从起始状态开始,若读到一个空字符,会在起始状态不断循环,直到遇到非空字符,状态转移情况如下:

    解析

    根据提取到的字符,转入不同的解析方法中,

    例如字符是t,说明值可能是true,只需检查后面三个字符,如果是r、u、e,则可以直接返回true。

    字符是f,说明值可能是false,只需检查后面四个字符,如果是a、l、s、e,则可以直接返回false。

    碰到 ”,说明是字符串,在下一个”出现之前,把扫描出来的字符都当成字符串中的字符,放到一个StringBuilder中去。

    碰到 [ 符号,说明是数组了,就需要new一个JsonArray,在下一个 ] 符号出现之前,调用nextObject方法,把解析到的对象都放到这个JsonArray里面去。

    碰到 { 符号,说明是JsonObject,就new一个JsonObject,这里每次需要连续调用两次nextObject,第一次结果作为key,第二次结果作为value。放到JsonObject中去。

    解析boolean、null值

    这类值的字符串只有固定的三种true、false、null,是最好解析的。在扫描到第一个字符为t、f、n时,只需检测后续字符是否符合固定值就可以了。checkChars方法实现了这个功能,chars是固定的序列,如果检测通过则返回true,否则返回false。

    private boolean checkChars(char ...chars){
            for(char ch : chars){
                char c = getCurrentCharNext();   //得到当前字符,包括空格、换行符。将指针指向下一个字符
                if(Character.toLowerCase(ch) != Character.toLowerCase(c)){
                    return false;
                }
            }
            return true;
        }

    如果是true,就是`checkChars('t','r','u','e')`

    解析数字

    解析数字的实现是parseNumber方法,我们先new一个StringBuilder,向后扫描只要碰到0-9或者+-小数点,就添加到这个StringBuilder当中去,否则就StringBuilder.toString,将这个字符串转换成数字。

    如果包含小数点,就用double,否则就用integer。

    解析字符串

    在json中字符串都是以双引号”开头,再以双引号”结尾的。当扫描到双引号”时,new一个StringBuilder,然后在下一个双引号”出现之前的每一个字符都需要添加到这个StringBuilder中去。需要注意的一点,字符串中是可能出现转义字符的。因此在扫描到一个字符为斜杠时,需要取出下一个字符进行特殊处理。

    解析JsonObject

    连续调用两次nextObject,第一次结果作为key,第二次结果作为value。放到JsonObject中去。
    注意逗号和冒号的处理。

    JsonArray的解析

     在下一个 ] 符号出现之前,递归调用nextObject方法,把解析到的对象都放到这个JsonArray里面去。

    返回

    由于nextObject只返回一个对象,我们用nextObject方法处理整个json字符串。那么nextObject方法就会得到你需要的JsonObject。

    超大json对象的解析

    参考 http://blog.csdn.net/dxiaolai/article/details/76359332

    在大数据量的json场景下,不必将整个json字符串全部解析成json object后再处理,而是通过迭代器模式我们可以在解析字符串的同时使用对象。这样可以大大的提高程序的执行效率。

    扩展ObjectParser类,使其成为一个迭代器,

    public class ObjectParser implements Iterator<Object>{
    
        public Object next(){
            return nextObject();
        }
    
        public boolean hasNext(){
            return pos < json.length();
        }
    
        @Override
        public void remove() {
    
        }
    
    }

    这样就可以边解析边使用对象了。

    ObjectParser parser = new ObjectParser ("json");
    while(parser.hasNext()){
        Object object = parser.next();
    }

    超大的json串,通常是以流的方式提供,我们不必要一次性将流字节全部读入内存,而是可以逐字符的解析。每次读取若干个字符,解析成对象;实现方式是使用BuffererReader,修改getChar等方法,每次读字符时从BuffererReader中读取。配合上面的迭代器模式,可考虑将一个BuffererReader封装成Iterator<Object>。

  • 相关阅读:
    利用avalon 实现一个简单的成绩单
    有关less 处理@arguments的一些高级技巧
    迷你MVVM框架 avalonjs 0.9发布
    IE9-10 option BUG
    判定元素正在插入到DOM树——DOMNodeInsertedIntoDocument
    迷你MVVM框架 avalonjs 0.85发布
    机器学习研究与开发平台的选择
    JS生成指定范围内的随机数(支持随机小数)
    DDD领域驱动设计 ---- 系列文章
    序列化效率比拼——谁是最后的赢家avaScriptSerializer方式、DataContract方式、Newtonsoft.Json
  • 原文地址:https://www.cnblogs.com/xcr1234/p/7860069.html
Copyright © 2011-2022 走看看