使用c#解析json库

写了个c#版的json解析库,提供了json到hashtable以及hashtable到json字符串的转换

受惠于c#的语法特性,hashtable到json的解析变得非常简单

先判断传入的object的类型,如果是hashtable或者array,则循环遍历,并且将元素转成字符串

得益于c#的语法,元素到字符串的转换可以以

Convert.ToString(value);

的方式简单完成.

整个hashtable到json串的转换代码如下:

        public static String pack(object dict)
        {
            Func<object, String> parsevalue = (object value) =>
            {
                String _out = "";
                if (value.GetType() == typeof(String))
                {
                    _out += "\"";
                }
                _out += Convert.ToString(value);
                if (value.GetType() == typeof(String))
                {
                    _out += "\"";
                }
                return _out;
            };

            Func<Array, String> parselist = (Array _array) =>
            {
                String _out = "]";
                foreach (Object o in _array)
                {
                    if ((o.GetType() == typeof(Hashtable)) || (o.GetType() == typeof(Array)))
                    {
                        _out += pack(o);
                    }
                    else
                    {
                        _out += parsevalue(o);
                    }
                    _out += ",";
                }
                _out.Remove(_out.Length - 1);
                _out += "]";

                return _out;
            };

            Func<Hashtable, String> parsedict = (Hashtable _dict) =>{
                String _out = "{";
                foreach (System.Collections.DictionaryEntry _obj in _dict)
                {
                    _out += "\"" + Convert.ToString(_obj.Key) + "\"";
                    _out += ":";
                    if ((_obj.Value.GetType() == typeof(Hashtable)) || (_obj.Value.GetType() == typeof(Array)))
                    {
                        _out += pack(_obj.Value);
                    }
                    else
                    {
                        _out += parsevalue(_obj.Value);
                    }
                    _out += ",";
                }
                _out.Remove(_out.Length - 1);
                _out += "}";

                return _out;
            };

            Func<object, String> parse = (object o) =>
            {
                if (o.GetType() == typeof(Hashtable))
                {
                    return parsedict((Hashtable)o);
                }
                else if (o.GetType() == typeof(Array))
                {
                    return parselist((Array)o);
                }
                else
                {
                    throw new System.Exception("can not parse this object to json");
                }
            };

            return parse(dict);
        }

可以如我在c++中惯用的做法一样,通过匿名函数将对hashtable,array以及其他值类型的转换分别拆分成了3个lambda对象parsevalue,parselist,parsedict.

 

对于json串转array或hashtable则稍微复杂,考虑到需要区分json是一个array([])还是hashtable({}),所以在删除了字符串前面的空格,table符号,以及其他无意义的符号之后首先判断了第一个字符串是"{"还是"[",并且进入对应的分支  

            if (c.Current.ToString() == "{")
                    {
                        parsesys = parsekey;
                        parseenum = parsemap;

                        Hashtable _newtable = new Hashtable();
                        if (_table != null)
                        {
                            key = key.Trim();
                            while (key[0] == '\n' || key[0] == '\t')
                            {
                                key = key.Substring(1, key.Length - 1);
                            }
                            s.Push(_table);
                            key = key.Substring(1, key.Length - 2);
                            _table[key] = _newtable;
                            _table = null;
                            key = "";
                        }
                        else if (_array != null)
                        {
                            s.Push(_array);
                            _array.Add(_newtable);
                            _array = null;
                        }
                        _table = _newtable;

                        continue;
                    }

                    if (c.Current.ToString() == "}")
                    {
                        parseenum(c.Current.ToString());

                        if (s.Count > 0)
                        {
                            parsepop(s.Pop());
                        }

                        continue;
                    }

                    if (c.Current.ToString() == "[")
                    {
                        parsesys = parsevalue;
                        parseenum = parsearray;

                        ArrayList _newarray = new ArrayList();
                        if (_table != null)
                        {
                            s.Push(_table);
                            key = key.Trim();
                            while (key[0] == '\n' || key[0] == '\t')
                            {
                                key = key.Substring(1, key.Length - 1);
                            }
                            key = key.Substring(1, key.Length - 2);
                            _table[key] = _newarray;
                            _table = null;
                            key = "";
                        }
                        else if (_array != null)
                        {
                            s.Push(_array);
                            _array.Add(_newarray);
                            _array = null;
                        }
                        _array = _newarray;

                        continue;
                    }

                    if (c.Current.ToString() == "]")
                    {
                        parseenum(c.Current.ToString());

                        parsepop(s.Pop());

                        continue;
                    }

然后整个对字符串的遍历过程,考虑json采用","分割元素,dict以及list都是,dict的元素是pair,对于pair采用":"分割key和value,而字符串则以""包括,所以我采用一个count计数来记录字符串中出现的"的次数,并且考虑到字符串中也有引号但是是以转义的方式比如"a\"1"的形式存在,所以用一个escape 记录出现的转义符

        int count = 0;
            int escape = 0;
            while (c.MoveNext())
            {
                if (c.Current.ToString() == "\\"){
                    escape = 1;
                }else{
                    escape = 0;
                }

                if (c.Current.ToString() == "\"" && escape != 1)
                {
                    if (count == 0)
                    {
                        count++;
                    }
                    else
                    {
                        count = 0;
                    }
                }

如此即可正确的识别当前字符是否是在一个字符串值中比如key或者类型为string的value

如果不是,即可按"," ":" "{" "}" "[" "]"这些json的语义符号的语义做语法处理,以此实现one pass解析json字符串。

然后考虑在解析时,会出现{},[]之间的嵌套,所以会在读取到{,[的时候分别创建新的hashtable或是array,并且将老的容器填入到一个stack中去,在读取到结束符号}或者]后,对stack做pop得到嵌套的上一层容器,并且对字符串做后续的解析。

整个解析字符串的代码如下:

        public static object unpack(String jsonstr)
        {
            object _out = null;
            Hashtable _table = null;
            ArrayList _array = null;

            String key = "";
            String value = "";
            CharEnumerator c = jsonstr.GetEnumerator();

            Stack s = new Stack();

            Func<String, String> parsekey = (String _c) =>{
                key += _c;
                return key;
            };

            Func<String, String> parsevalue = (String _c) =>
            {
                value += _c;
                return value;
            };

            Func<String, String> parsesys = parsekey;

            Func<String, String> parsemap = (String _c) =>
            {
                parsesys = parsekey;

                if (value == "" || key == "")
                {
                    return _c;
                }

                value = value.Trim();
                while (value[0] == '\n' || value[0] == '\t')
                {
                    value = value.Substring(1, value.Length - 1);
                }
                String v = value;
                key = key.Trim();
                while (key[0] == '\n' || key[0] == '\t')
                {
                    key = key.Substring(1, key.Length - 1);
                }
                key = key.Substring(1, key.Length - 2);

                if (v == "true")
                {
                    _table[key] = true;
                }
                else if (v == "false")
                {
                    _table[key] = false;
                }
                else if (v == "null")
                {
                    _table[key] = null;
                }
                else
                {
                    if (v[0] == '\"' && v[v.Length - 1] == '\"')
                    {
                        v = v.Substring(1, v.Length - 2);
                        _table[key] = v;
                    }
                    else
                    {
                        int status = 0;

                        foreach (char _ch in v)
                        {
                            if ((_ch < '0' || _ch > '9') && _ch != '.')
                            {
                                throw new Exception("format error");
                            }

                            if (_ch == '.')
                            {
                                status++;
                            }
                        }

                        if (status == 0)
                        {
                            _table[key] = Convert.ToInt64(v);
                        }
                        else if (status == 1)
                        {
                            _table[key] = Convert.ToDouble(v);
                        }
                        else
                        {
                            throw new Exception("format error");
                        }
                    }
                    
                }

                key = "";
                value = "";

                return _c;
            };

            Func<String, String> parsearray = (String _c) =>
            {
                value = value.Trim();

                if (value == "")
                {
                    return _c;
                }

                while (value[0] == '\n' || value[0] == '\t')
                {
                    value = value.Substring(1, value.Length - 1);
                }
                String v = value;

                if (v.ToLower() == "true")
                {
                    _array.Add(true);
                }
                else if (v.ToLower() == "false")
                {
                    _array.Add(false);
                }
                else if (v.ToLower() == "null")
                {
                    _array.Add(null);
                }
                else
                {
                    if (v[0] == '\"' && v[v.Length - 1] == '\"')
                    {
                        v = v.Substring(1, v.Length - 2);
                        _array.Add(v);
                    }
                    else
                    {
                        int status = 0;

                        foreach (char _ch in v)
                        {
                            if ((_ch < '0' || _ch > '9') && _ch != '.')
                            {
                                throw new Exception("format error");
                            }

                            if (_ch == '.')
                            {
                                status++;
                            }
                        }

                        if (status == 0)
                        {
                            _array.Add(Convert.ToInt64(v));
                        }
                        else if (status == 1)
                        {
                            _array.Add(Convert.ToDouble(v));
                        }
                        else
                        {
                            throw new Exception("format error");
                        }
                    }

                }

                key = "";
                value = "";

                return _c;
            };

            Func<String, String> parseenum = parsemap;

            Func<object, object> parsepop = (object o) =>{
                if (o.GetType() == typeof(Hashtable))
                {
                    _table = (Hashtable)o;

                    parsesys = parsekey;
                    parseenum = parsemap;
                }
                else if (o.GetType() == typeof(ArrayList))
                {
                    _array = (ArrayList)o;

                    parsesys = parsevalue;
                    parseenum = parsearray;
                }

                return o;
            };

            int count = 0;
            int escape = 0;
            while (c.MoveNext())
            {
                if (c.Current.ToString() == "\\"){
                    escape = 1;
                }else{
                    escape = 0;
                }

                if (c.Current.ToString() == "\"" && escape != 1)
                {
                    if (count == 0)
                    {
                        count++;
                    }
                    else
                    {
                        count = 0;
                    }
                }

                if (count == 0)
                {
                    if (c.Current.ToString() == "{")
                    {
                        parsesys = parsekey;
                        parseenum = parsemap;

                        Hashtable _newtable = new Hashtable();
                        if (_table != null)
                        {
                            key = key.Trim();
                            while (key[0] == '\n' || key[0] == '\t')
                            {
                                key = key.Substring(1, key.Length - 1);
                            }
                            s.Push(_table);
                            key = key.Substring(1, key.Length - 2);
                            _table[key] = _newtable;
                            _table = null;
                            key = "";
                        }
                        else if (_array != null)
                        {
                            s.Push(_array);
                            _array.Add(_newtable);
                            _array = null;
                        }
                        _table = _newtable;

                        continue;
                    }

                    if (c.Current.ToString() == "}")
                    {
                        parseenum(c.Current.ToString());

                        if (s.Count > 0)
                        {
                            parsepop(s.Pop());
                        }

                        continue;
                    }

                    if (c.Current.ToString() == "[")
                    {
                        parsesys = parsevalue;
                        parseenum = parsearray;

                        ArrayList _newarray = new ArrayList();
                        if (_table != null)
                        {
                            s.Push(_table);
                            key = key.Trim();
                            while (key[0] == '\n' || key[0] == '\t')
                            {
                                key = key.Substring(1, key.Length - 1);
                            }
                            key = key.Substring(1, key.Length - 2);
                            _table[key] = _newarray;
                            _table = null;
                            key = "";
                        }
                        else if (_array != null)
                        {
                            s.Push(_array);
                            _array.Add(_newarray);
                            _array = null;
                        }
                        _array = _newarray;

                        continue;
                    }

                    if (c.Current.ToString() == "]")
                    {
                        parseenum(c.Current.ToString());

                        parsepop(s.Pop());

                        continue;
                    }

                    if (c.Current.ToString() == ",")
                    {
                        parseenum(c.Current.ToString());
                        continue;
                    }

                    if (c.Current.ToString() == ":")
                    {
                        parsesys = parsevalue;
                        continue;
                    }
                }

                parsesys(c.Current.ToString());

            }

            if (_table != null)
            {
                _out = _table;
            }
            else if (_array != null)
            {
                _out = _array;
            }

            return _out;
        }

代码地址: https://github.com/qianqians/jsonparse/blob/master/JsonParser.cs

这份代码,目前还是只是在一个很小的unity项目中使用,希望更多的朋友使用并提交bug。

posted @ 2015-05-13 22:18  芊芊水  阅读(2758)  评论(0编辑  收藏  举报