json解析源码学习

就当笔记吧 / 2023-08-13 / 原文

c#的几个Json库

MiniJSON

SimpleJson

litjson

NewtonJson

 

其中MiniJSON最简单,所以这边也是学习这个库的Json解析部分(注意:只涉及解析,没有生成json)。

整体代码也没有用到特别的算法什么的,就是一个一个字符的读取,然后根据读到的边界标识符来进行后续的读取,边界标识符的话就是:{}、[]、""、:、,等。

单引号、注释这些的话没有支持,加上各种兼容性估计代码就会复杂很多。

/*
 * http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html
 *
 * Simplified it so that it doesn't throw exceptions
 * and can be used in Unity iPhone with maximum code stripping.
 *
 */
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;

public static class Json
{
    /// <summary>
    /// Parses the string json into a value
    /// </summary>
    /// <param name="json">A JSON string.</param>
    /// <returns>An List&lt;object&gt;, a Dictionary&lt;string, object&gt;, a double, an integer,a string, null, true, or false</returns>
    public static object Deserialize(string json)
    {
        if (json == null)
            return null;
        return Parser.Parse(json);
    }


    private sealed class Parser : IDisposable
    {
        private const string WORD_BREAK = "{}[],:\"";
        private StringReader json;

        private Parser(string jsonString)
        {
            json = new StringReader(jsonString);
        }

        /// <summary>
        /// 探查下一个字符(读指针不变)
        /// </summary>
        private char PeekChar
        {
            get { return Convert.ToChar(json.Peek()); }
        }

        /// <summary>
        /// 读下一个字符(读指针往后+1)
        /// </summary>
        private char NextChar
        {
            get { return Convert.ToChar(json.Read()); }
        }

        private string NextWord
        {
            get
            {
                var word = new StringBuilder();

                while (!IsWordBreak(PeekChar))
                {
                    word.Append(NextChar);

                    if (json.Peek() == -1)
                        break;
                }

                return word.ToString();
            }
        }

        /// <summary>
        /// 通过1个字符, 确定标识符, 然后以此确定接下来怎么做
        /// </summary>
        private TOKEN NextToken
        {
            get
            {
                EatWhitespace();

                if (json.Peek() == -1)
                    return TOKEN.NONE;

                switch (PeekChar)
                {
                case '{':
                    return TOKEN.CURLY_OPEN;
                case '}':
                    json.Read();
                    return TOKEN.CURLY_CLOSE;

                case '[':
                    return TOKEN.SQUARED_OPEN;
                case ']':
                    json.Read();
                    return TOKEN.SQUARED_CLOSE;

                case ',':
                    json.Read();
                    return TOKEN.COMMA;
                case '"':
                    return TOKEN.STRING;
                case ':':
                    return TOKEN.COLON;
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                case '-':
                    return TOKEN.NUMBER;
                }

                switch (NextWord)
                {
                case "false":
                    return TOKEN.FALSE;
                case "true":
                    return TOKEN.TRUE;
                case "null":
                    return TOKEN.NULL;
                }

                //jsonStr="abc", 就是none的
                return TOKEN.NONE;
            }
        }

        public void Dispose()
        {
            json.Dispose();
            json = null;
        }


        /// <summary>
        /// 断词字符: abc: abc ,abc{, abc}, abc[, abc]等
        /// </summary>
        public static bool IsWordBreak(char c)
        {
            return char.IsWhiteSpace(c) || WORD_BREAK.IndexOf(c) != -1;
        }

        public static object Parse(string jsonString)
        {
            using (var instance = new Parser(jsonString))
            {
                return instance.ParseValue();
            }
        }

        private Dictionary<string, object> ParseObject()
        {
            var table = new Dictionary<string, object>();

            // ditch opening brace
            json.Read();

            // {
            while (true)
            {
                switch (NextToken)
                {
                case TOKEN.NONE:
                    // {abc} //不合法object, 得到null
                    return null;
                case TOKEN.COMMA: // {,, "key":1}也不会报错
                    continue;
                case TOKEN.CURLY_CLOSE:
                    return table;
                default:
                    // {false} //ParseString()拿到空, 然后EatWhiteSpace报错
                    // {012} // ParseString()拿到012}, 然后EatWhiteSpace报错
                    // {{}} //报错
                    // {[]} //报错

                    // name
                    var keyName = ParseString();
                    if (keyName == null)
                    {
                        return null;
                    }
                    Debug.Log($"'{keyName}'");

                    // :
                    if (NextToken != TOKEN.COLON) // {"abc"123}这样的情况, 无效的对象
                        return null;

                    // ditch the colon
                    json.Read();

                    // value
                    table[keyName] = ParseValue();
                    break;
                }
            }
        }

        private List<object> ParseArray()
        {
            var array = new List<object>();

            // ditch opening bracket
            json.Read();

            // [
            var parsing = true;
            while (parsing)
            {
                var nextToken = NextToken;

                switch (nextToken)
                {
                case TOKEN.NONE:
                    // [abc] //不合法array, 得到null
                    return null;
                case TOKEN.COMMA: // {,, 123}不会报错, 最终能得到正确的array
                    continue;
                case TOKEN.SQUARED_CLOSE:
                    parsing = false;
                    break;

                default:
                    var value = ParseByToken(nextToken);

                    array.Add(value);
                    break;
                }
            }

            return array;
        }

        //解析json的下一个值(number, bool, string, unicode, null, object, array)
        private object ParseValue()
        {
            var nextToken = NextToken;
            return ParseByToken(nextToken);
        }

        //根据开始token读后面的值
        private object ParseByToken(TOKEN token)
        {
            switch (token)
            {
            case TOKEN.STRING:
                return ParseString();
            case TOKEN.NUMBER:
                return ParseNumber();
            case TOKEN.CURLY_OPEN:
                return ParseObject();
            case TOKEN.SQUARED_OPEN:
                return ParseArray();
            case TOKEN.TRUE:
                return true;
            case TOKEN.FALSE:
                return false;
            case TOKEN.NULL:
                return null;
            default:
                return null;
            }
        }

        /// <summary>
        /// Token为STRING时, 调用该函数才正常
        /// </summary>
        private string ParseString()
        {
            var s = new StringBuilder();
            char c;

            // ditch opening quote
            json.Read();

            var parsing = true;
            while (parsing)
            {
                if (json.Peek() == -1)
                {
                    parsing = false;
                    break;
                }

                c = NextChar;
                switch (c)
                {
                case '"': //结束双引号
                    parsing = false;
                    break;

                case '\\': // 这样? { "key":"abc\
                    if (json.Peek() == -1)
                    {
                        parsing = false;
                        break;
                    }

                    c = NextChar;
                    switch (c)
                    {
                    case '"': // \"就是Append(")
                    case '\\': // \\就是Append(\)
                    case '/': // \/就是Append(/)
                        s.Append(c);
                        break;
                    case 'b':
                        s.Append('\b');
                        break;
                    case 'f':
                        s.Append('\f');
                        break;
                    case 'n':
                        s.Append('\n'); //linux换行
                        break;
                    case 'r':
                        s.Append('\r'); //mac换行
                        break;
                    case 't':
                        s.Append('\t'); //tab
                        break;
                    case 'u': // "\u012"会报错
                        var hex = new char[4];
                        for (var i = 0; i < 4; i++)
                            hex[i] = NextChar;

                        s.Append((char)Convert.ToInt32(new string(hex), 16));
                        break;
                    }
                    break;
                default:
                    s.Append(c);
                    break;
                }
            }

            Debug.Log($"ParseString: {s.ToString()}");
            return s.ToString();
        }

        private object ParseNumber()
        {
            var number = NextWord;

            // {"abc": 1-23}, 不会报错, 会得到abc的值为0
            if (number.IndexOf('.') == -1)
            {
                long parsedInt;
                long.TryParse(number, out parsedInt);
                Debug.Log($"ParseNumber: str:{number}, val:{parsedInt}");
                return parsedInt;
            }

            double parsedDouble;
            double.TryParse(number, out parsedDouble);
            Debug.Log($"ParseNumber: str:{number}, val:{parsedDouble}");
            return parsedDouble;
        }

        private void EatWhitespace()
        {
            while (char.IsWhiteSpace(PeekChar))
            {
                json.Read();

                if (json.Peek() == -1)
                    break;
            }
        }

        private enum TOKEN
        {
            NONE,

            CURLY_OPEN, // {
            CURLY_CLOSE, // }

            SQUARED_OPEN, // [
            SQUARED_CLOSE, // ]

            COLON, // :
            COMMA, // ,
            STRING, // "
            NUMBER, // 负号或数字
            TRUE, // true
            FALSE, // false
            NULL, // null
        };
    }

}
 

 

参考

用C#实现一个迷你json库,无需引入dll(可直接放到Unity中使用)_林新发的博客-CSDN博客