예제 #1
0
        private static Json RunParse(TrackedStringManager tracker, ParserRules rules)
        {
            Json output = new Json(tracker);

            try
            {
                if (tracker.Text == null)
                {
                    output.AddError("Null source text provided!");
                    tracker.HasChanges = false;
                    return(output);
                }

                JsonTextIndexer indexer = new JsonTextIndexer(tracker.Text, rules);
                // TODO: Handle case where root is document without braces( ie "item : value, item2 : value2")

                int indexOfFirstOpenBracket = indexer.NextAny(0, '{', '[');

                // ============ No Brackets ===========
                if (indexOfFirstOpenBracket == -1)
                {
                    output.Data        = new JsonValue(output.TextTracking, 0, indexer.NextAny(0, '}', ']'), false);
                    tracker.HasChanges = false;
                    return(output);
                }

                // ============ Has Brackets ===========
                char nextBracket = output.TextTracking.Text[indexOfFirstOpenBracket];
                if (nextBracket == '{')
                {
                    output.Data        = new JsonDocument(indexer, output.TextTracking, indexOfFirstOpenBracket, out int endIndex);
                    tracker.HasChanges = false;
                    return(output);
                }
                else // if(nextBracket == '[')
                {
                    output.Data        = new JsonArray(indexer, output.TextTracking, indexOfFirstOpenBracket, out int endIndex);
                    tracker.HasChanges = false;
                    return(output);
                }
            }
            catch (Exception exc)
            {
                output.AddError(exc.ToString());
            }

            return(output);
        }
예제 #2
0
        // ===================[ Construction & Parsing ]===========================
        /// <summary>
        /// Constructor for parsing
        /// </summary>
        internal JsonArray(JsonTextIndexer indexer, TrackedStringManager source, int startIndex, out int endIndex) : base(source)
        {
            endIndex = -1;

            int  nextEval   = indexer.NextAny(startIndex + 1);
            int  lastStart  = startIndex + 1;
            int  quoteStart = -1;
            bool lastWasUseful;

            while (nextEval != -1)
            {
                lastWasUseful = true;
                switch (source.Text[nextEval])
                {
                case ']':
                    if (quoteStart != -1)
                    {
                        break;
                    }

                    // There can be an unquoted array item inbetween the last comma'd item and the end
                    //  bracket. To make sure we handle that case, but don't add empty elements, we're
                    //  going to check it's an empty string before we add it.
                    //
                    // While empty keys and values will be supported (keys as long as they are unique)
                    //  they must be quoted so we can tell when the item is supposed to end.
                    if (lastStart != nextEval)
                    {
                        this.Add(new JsonValue(source, lastStart, nextEval - 1, false));
                    }

                    endIndex = nextEval;
                    break;

                case '[':
                {
                    int endOfArr;
                    this.Add(new JsonArray(indexer, source, nextEval, out endOfArr));
                    if (endOfArr == -1)
                    {
                        throw new FormatException($"Invalid json format provided, issue starts with array that begins at text index {nextEval}");
                    }

                    int peekAheadInd = indexer.NextAny(endOfArr + 1);
                    if (peekAheadInd != -1 && source.Text[peekAheadInd] == ',')
                    {
                        nextEval = peekAheadInd;
                    }
                    else
                    {
                        nextEval = endOfArr;
                    }
                }
                break;

                case '{':
                {
                    if (quoteStart != -1)
                    {
                        break;
                    }

                    int endOfDoc;
                    this.Add(new JsonDocument(indexer, source, nextEval, out endOfDoc));
                    if (endOfDoc == -1)
                    {
                        throw new FormatException($"Invalid json format provided, issue starts with document that begins at text index {nextEval}");
                    }

                    int peekAheadInd = indexer.NextAny(endOfDoc + 1);
                    if (peekAheadInd != -1 && source.Text[peekAheadInd] == ',')
                    {
                        nextEval = peekAheadInd;
                    }
                    else
                    {
                        nextEval = endOfDoc;
                    }
                }
                break;

                case ',':
                    if (quoteStart != -1)
                    {
                        break;
                    }

                    // We're safe to assume we can add this here because commas after docs are covered above
                    this.Add(new JsonValue(source, lastStart, nextEval - 1, false));
                    break;

                case '\"':
                    if (quoteStart != -1)
                    {
                        this.Add(new JsonValue(source, quoteStart, nextEval - 1, true));
                        quoteStart = -1;

                        int peekAheadInd = indexer.NextAny(nextEval + 1);
                        if (peekAheadInd != -1 && source.Text[peekAheadInd] == ',')
                        {
                            nextEval = peekAheadInd;
                        }
                    }
                    else
                    {
                        quoteStart = nextEval + 1;
                    }
                    break;

                default:
                    lastWasUseful = false;
                    break;
                }

                if (endIndex != -1)
                {
                    break;
                }

                if (lastWasUseful)
                {
                    lastStart = nextEval + 1;
                }
                nextEval = indexer.NextAny(nextEval + 1);
            }

            this.ResetStringValue(startIndex, endIndex);
            this.Source.Track(this);
        }
예제 #3
0
        // =========================[ Constructor & Parsing ]===============================
        /// <summary>
        /// Constructor for parsing
        /// </summary>
        internal JsonDocument(JsonTextIndexer textIndexer, TrackedStringManager source, int startIndex, out int endIndex) : base(source)
        {
            endIndex = -1;

            int nextEval = textIndexer.NextAny(startIndex + 1);

            JsonHelper.IndexTrackingHelper keyIndexTracker = new JsonHelper.IndexTrackingHelper();
            int  lastStart        = startIndex + 1;
            int  insideQuoteStart = -1;
            bool lastWasUseful;

            while (nextEval != -1)
            {
                lastWasUseful = true;
                switch (source.Text[nextEval])
                {
                case '}':
                    if (insideQuoteStart != -1)
                    {
                        break;
                    }

                    if (keyIndexTracker)
                    {
                        this.Add(keyIndexTracker.CreateTS(source), new JsonValue(source, lastStart, nextEval - 1, false));
                    }
                    endIndex = nextEval;
                    break;

                case '{':
                {
                    if (insideQuoteStart != -1)
                    {
                        break;
                    }

                    int endOfDoc;

                    if (!keyIndexTracker)
                    {
                        throw new FormatException("Can't read json format - error found around  offset: " + nextEval.ToString());
                    }

                    this.Add(keyIndexTracker.CreateTS(source),
                             new JsonDocument(textIndexer, source, nextEval, out endOfDoc));
                    if (endOfDoc == -1)
                    {
                        throw new FormatException($"Invalid json format provided, issue starts with document that begins at text index {nextEval}");
                    }

                    nextEval = endOfDoc;
                    keyIndexTracker.Reset();
                }
                break;

                case '[':
                {
                    if (insideQuoteStart != -1)
                    {
                        break;
                    }

                    if (!keyIndexTracker)
                    {
                        throw new FormatException("Can't read json format - attempted to add empty string key");
                    }

                    this.Add(keyIndexTracker.CreateTS(source),
                             new JsonArray(textIndexer, source, nextEval, out int endOfArr));
                    if (endOfArr == -1)
                    {
                        throw new FormatException($"Invalid json format provided, issue starts with array that begins at text index {nextEval}");
                    }

                    nextEval = endOfArr;
                    keyIndexTracker.Reset();
                }
                break;

                case ':':
                    if (insideQuoteStart != -1)
                    {
                        break;
                    }

                    keyIndexTracker.StartIndex = lastStart;
                    keyIndexTracker.EndIndex   = nextEval - 1;
                    break;

                case '\"':
                    // Start quote
                    if (insideQuoteStart == -1)
                    {
                        insideQuoteStart = nextEval + 1;
                    }
                    // End quote
                    else
                    {
                        // Peek ahead so we can decide if it's a key or a value
                        // If the next thing is a colon, then it's a key, otherwise it's a value
                        int peekAheadInd = textIndexer.NextAny(nextEval + 1);
                        if (peekAheadInd != -1 && source.Text[peekAheadInd] == ':')
                        {
                            keyIndexTracker.StartIndex     = insideQuoteStart;
                            keyIndexTracker.EndIndex       = nextEval - 1; // One before the quote that was just found
                            keyIndexTracker.IsInsideQuotes = true;
                            nextEval = peekAheadInd;
                        }
                        else
                        {
                            if (!keyIndexTracker)
                            {
                                string quote = source.Text.Substring(insideQuoteStart, nextEval - 1);
                                throw new FormatException("Json parsing failed due to incorrect string formatting around string: " + quote);
                            }

                            this.Add(keyIndexTracker.CreateTS(source), new JsonValue(source, insideQuoteStart, nextEval - 1, true));
                            keyIndexTracker.Reset();
                        }

                        insideQuoteStart = -1;
                    }

                    break;

                case ',':
                    if (insideQuoteStart != -1)
                    {
                        break;
                    }

                    if (keyIndexTracker)
                    {
                        var endValue = new JsonValue(source, lastStart, nextEval - 1, false);
                        if (endValue.StringValue.Length != 0)
                        {
                            this.Add(keyIndexTracker.CreateTS(source), endValue);
                            keyIndexTracker.Reset();
                        }
                    }
                    break;

                default:
                    lastWasUseful = false;
                    break;
                }

                if (endIndex != -1)
                {
                    break;
                }

                if (lastWasUseful)
                {
                    lastStart = nextEval + 1;
                }
                nextEval = textIndexer.NextAny(nextEval + 1);
            }

            if (nextEval == -1)
            {
                throw new FormatException("Json was improperly formatted");
            }

            this.ResetStringValue(startIndex, endIndex);
            this.Source.Track(this);
        }