Ejemplo n.º 1
0
        /// <summary>
        /// Parse a JSON string to generate objects.
        /// </summary>
        /// <param name="json">The JSON string</param>
        /// <returns>A .NET object</returns>
        public static JNode Parse(string json)
        {
            int rp   = 0;
            int rmax = json.Length;

            int[] istack = new int[32];
            int   isp    = 0;

            object[] ostack = new object[32];
            int      osp    = 0;
            int      state  = ROOT;

            string  key  = null;
            JArray  list = null;
            JObject hash = null;
            JNode   item = null;

            StringBuilder sb;
            char          ctmp;
            int           itmp, jtmp;

            while (true)
            {
                while (rp < rmax && char.IsWhiteSpace(json [rp]))
                {
                    rp++;
                }
                if (rp == rmax)
                {
                    throw new JSONParseException("unexpected EOF at " + rp);
                }

                switch (json [rp])
                {
                case '[':
                    if (isp + 2 > istack.Length)
                    {
                        Array.Resize(ref istack, isp * 2);
                    }
                    if (osp + 2 > ostack.Length)
                    {
                        Array.Resize(ref ostack, osp * 2);
                    }
                    istack [isp++] = state;
                    ostack [osp++] = list;

                    list  = new JArray();
                    state = LIST_START;
                    rp++;
                    break;

                case '{':
                    if (isp + 2 > istack.Length)
                    {
                        Array.Resize(ref istack, isp * 2);
                    }
                    if (osp + 2 > ostack.Length)
                    {
                        Array.Resize(ref ostack, osp * 2);
                    }
                    istack [isp++] = state;
                    ostack [osp++] = key;
                    ostack [osp++] = hash;

                    hash  = new JObject();
                    state = HASH_START;
                    rp++;
                    break;

                case ']':
                    if (state == LIST_START || state == LIST_ITEM)
                    {
                        rp++;
                        item  = list;
                        list  = (JArray)ostack [--osp];
                        state = istack [--isp];
                        goto add_item;
                    }
                    else
                    {
                        throw new JSONParseException("unexpected CLOSE BRACKET at " + rp);
                    }

                case '}':
                    if (state == HASH_START || state == HASH_ITEM)
                    {
                        rp++;
                        item  = hash;
                        hash  = (JObject)ostack [--osp];
                        key   = (string)ostack [--osp];
                        state = istack [--isp];
                        goto add_item;
                    }
                    else
                    {
                        throw new JSONParseException("unexpected CLOSE BRACE at " + rp);
                    }

                case ':':
                    if (state == HASH_KEY)
                    {
                        state = HASH_COLON;
                    }
                    else
                    {
                        throw new JSONParseException("unexpected COLON at " + rp);
                    }
                    rp++;
                    break;

                case ',':
                    if (state == HASH_ITEM)
                    {
                        state = HASH_COMMA;
                    }
                    else if (state == LIST_ITEM)
                    {
                        state = LIST_COMMA;
                    }
                    else
                    {
                        throw new JSONParseException("unexpected COMMA at " + rp);
                    }
                    rp++;
                    break;

                case 't':
                    if (rp + 4 <= rmax && json.Substring(rp, 4) == "true")
                    {
                        item = true;
                        rp  += 4;
                        goto add_item;
                    }
                    goto default;

                case 'f':
                    if (rp + 5 <= rmax && json.Substring(rp, 5) == "false")
                    {
                        item = false;
                        rp  += 5;
                        goto add_item;
                    }
                    goto default;

                case 'n':
                    if (rp + 4 <= rmax && json.Substring(rp, 4) == "null")
                    {
                        item = null;
                        rp  += 4;
                        goto add_item;
                    }
                    goto default;

                case '"':
                    rp++;

                    sb = new StringBuilder();
                    while (true)
                    {
                        if (rp == rmax)
                        {
                            throw new JSONParseException("unexpected EOF in string at " + rp);
                        }
                        ctmp = json [rp];

                        if (char.IsControl(ctmp))
                        {
                            throw new JSONParseException("control character in string at " + rp);
                        }
                        rp++;

                        if (ctmp == '"')
                        {
                            break;
                        }
                        if (ctmp != '\\')
                        {
                            sb.Append(ctmp);
                            continue;
                        }
                        if (rp == rmax)
                        {
                            throw new JSONParseException("unexpected EOF in string at " + rp);
                        }

                        switch (json [rp++])
                        {
                        case '"':
                            sb.Append('"');
                            break;

                        case '\\':
                            sb.Append('\\');
                            break;

                        case '/':
                            sb.Append('/');
                            break;

                        case 'b':
                            sb.Append('\b');
                            break;

                        case 'f':
                            sb.Append('\f');
                            break;

                        case 'n':
                            sb.Append('\n');
                            break;

                        case 'r':
                            sb.Append('\r');
                            break;

                        case 't':
                            sb.Append('\t');
                            break;

                        case 'u':
                            if (rp + 4 > rmax)
                            {
                                throw new JSONParseException("unexpected EOF in hex escape at " + rp);
                            }
                            jtmp = 0;
                            for (itmp = 0; itmp < 4; itmp++)
                            {
                                ctmp = json [rp++];
                                jtmp = (jtmp << 4) + (int)ctmp;
                                if (ctmp >= '0' && ctmp <= '9')
                                {
                                    jtmp -= (int)'0';
                                }
                                else if (ctmp >= 'a' && ctmp <= 'f')
                                {
                                    jtmp -= ((int)'a' - 10);
                                }
                                else if (ctmp >= 'A' && ctmp <= 'F')
                                {
                                    jtmp -= ((int)'A' - 10);
                                }
                                else
                                {
                                    throw new JSONParseException("invalid hex digit at " + rp);
                                }
                            }
                            sb.Append((char)jtmp);
                            break;

                        default:
                            throw new JSONParseException("unknown backslash escape at " + rp);
                        }
                    }
                    item = sb.ToString();
                    goto add_item;

                case '-':
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    itmp = rp;
                    if (rp < rmax && json [rp] == '-')
                    {
                        rp++;
                    }

                    if (rp < rmax && json [rp] == '0')
                    {
                        rp++;
                    }
                    else if (rp < rmax && json [rp] >= '1' && json [rp] <= '9')
                    {
                        rp++;
                        while (rp < rmax && json [rp] >= '0' && json [rp] <= '9')
                        {
                            rp++;
                        }
                    }
                    else
                    {
                        throw new JSONParseException("malformed number at " + rp);
                    }

                    if (rp < rmax && json [rp] == '.')
                    {
                        rp++;
                        if (rp < rmax && json [rp] >= '0' && json [rp] <= '9')
                        {
                            rp++;
                        }
                        else
                        {
                            throw new JSONParseException("malformed number at " + rp);
                        }

                        while (rp < rmax && json [rp] >= '0' && json [rp] <= '9')
                        {
                            rp++;
                        }
                    }

                    if (rp < rmax && (json [rp] == 'e' || json [rp] == 'E'))
                    {
                        rp++;
                        if (rp < rmax && (json [rp] == '+' || json [rp] == '-'))
                        {
                            rp++;
                        }

                        if (rp < rmax && json [rp] >= '0' && json [rp] <= '9')
                        {
                            rp++;
                        }
                        else
                        {
                            throw new JSONParseException("malformed number at " + rp);
                        }

                        while (rp < rmax && json [rp] >= '0' && json [rp] <= '9')
                        {
                            rp++;
                        }
                    }
                    item = double.Parse(json.Substring(itmp, rp - itmp), NumberFormatInfo.InvariantInfo);
                    goto add_item;

                default:
                    throw new JSONParseException("unrecognized token at " + rp);
                }
                continue;
add_item:
                if (state == HASH_START || state == HASH_COMMA)
                {
                    JString js = item as JString;
                    if (js == null)
                    {
                        throw new JSONParseException("non-string hash key at " + rp);
                    }
                    js.TryString(out key);
                    state = HASH_KEY;
                }
                else if (state == HASH_COLON)
                {
                    hash [key] = item;
                    state      = HASH_ITEM;
                }
                else if (state == LIST_START || state == LIST_COMMA)
                {
                    list.Add(item);
                    state = LIST_ITEM;
                }
                else if (state == ROOT)
                {
                    while (rp < rmax && char.IsWhiteSpace(json [rp]))
                    {
                        rp++;
                    }
                    if (rp < rmax)
                    {
                        throw new JSONParseException("trailing garbage at " + rp);
                    }
                    return(item);
                }
                else
                {
                    throw new JSONParseException("unexpected ITEM at " + rp);
                }
                continue;
            }
        }