#region License // Copyright (c) 2007 James Newton-King // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. #endregion using System; using System.Collections.Generic; using System.IO; using System.Globalization; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Utilities; namespace Newtonsoft.Json { /// /// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. /// public abstract class JsonReader : IDisposable { /// /// Specifies the state of the reader. /// protected enum State { /// /// The Read method has not been called. /// Start, /// /// The end of the file has been reached successfully. /// Complete, /// /// Reader is at a property. /// Property, /// /// Reader is at the start of an object. /// ObjectStart, /// /// Reader is in an object. /// Object, /// /// Reader is at the start of an array. /// ArrayStart, /// /// Reader is in an array. /// Array, /// /// The Close method has been called. /// Closed, /// /// Reader has just read a value. /// PostValue, /// /// Reader is at the start of a constructor. /// ConstructorStart, /// /// Reader in a constructor. /// Constructor, /// /// An error occurred that prevents the read operation from continuing. /// Error, /// /// The end of the file has been reached successfully. /// Finished } // current Token data private JsonToken _token; private object _value; private Type _valueType; private char _quoteChar; private State _currentState; private JTokenType _currentTypeContext; /// /// Gets the current reader state. /// /// The current reader state. protected State CurrentState { get { return _currentState; } } private int _top; private readonly List _stack; /// /// Gets the quotation mark character used to enclose the value of a string. /// public virtual char QuoteChar { get { return _quoteChar; } protected internal set { _quoteChar = value; } } /// /// Gets the type of the current Json token. /// public virtual JsonToken TokenType { get { return _token; } } /// /// Gets the text value of the current Json token. /// public virtual object Value { get { return _value; } } /// /// Gets The Common Language Runtime (CLR) type for the current Json token. /// public virtual Type ValueType { get { return _valueType; } } /// /// Gets the depth of the current token in the JSON document. /// /// The depth of the current token in the JSON document. public virtual int Depth { get { int depth = _top - 1; if (IsStartToken(TokenType)) return depth - 1; else return depth; } } /// /// Initializes a new instance of the class with the specified . /// protected JsonReader() { _currentState = State.Start; _stack = new List(); Push(JTokenType.None); } private void Push(JTokenType value) { _stack.Add(value); _top++; _currentTypeContext = value; } private JTokenType Pop() { JTokenType value = Peek(); _stack.RemoveAt(_stack.Count - 1); _top--; _currentTypeContext = _stack[_top - 1]; return value; } private JTokenType Peek() { return _currentTypeContext; } /// /// Reads the next JSON token from the stream. /// /// true if the next token was read successfully; false if there are no more tokens to read. public abstract bool Read(); /// /// Reads the next JSON token from the stream as a . /// /// A or a null reference if the next JSON token is null. public abstract byte[] ReadAsBytes(); /// /// Skips the children of the current token. /// public void Skip() { if (IsStartToken(TokenType)) { int depth = Depth; while (Read() && (depth < Depth)) { } } } /// /// Sets the current token. /// /// The new token. protected void SetToken(JsonToken newToken) { SetToken(newToken, null); } /// /// Sets the current token and value. /// /// The new token. /// The value. protected virtual void SetToken(JsonToken newToken, object value) { _token = newToken; switch (newToken) { case JsonToken.StartObject: _currentState = State.ObjectStart; Push(JTokenType.Object); break; case JsonToken.StartArray: _currentState = State.ArrayStart; Push(JTokenType.Array); break; case JsonToken.StartConstructor: _currentState = State.ConstructorStart; Push(JTokenType.Constructor); break; case JsonToken.EndObject: ValidateEnd(JsonToken.EndObject); _currentState = State.PostValue; break; case JsonToken.EndArray: ValidateEnd(JsonToken.EndArray); _currentState = State.PostValue; break; case JsonToken.EndConstructor: ValidateEnd(JsonToken.EndConstructor); _currentState = State.PostValue; break; case JsonToken.PropertyName: _currentState = State.Property; Push(JTokenType.Property); break; case JsonToken.Undefined: case JsonToken.Integer: case JsonToken.Float: case JsonToken.Boolean: case JsonToken.Null: case JsonToken.Date: case JsonToken.String: case JsonToken.Raw: case JsonToken.Bytes: _currentState = State.PostValue; break; } JTokenType current = Peek(); if (current == JTokenType.Property && _currentState == State.PostValue) Pop(); if (value != null) { _value = value; _valueType = value.GetType(); } else { _value = null; _valueType = null; } } private void ValidateEnd(JsonToken endToken) { JTokenType currentObject = Pop(); if (GetTypeForCloseToken(endToken) != currentObject) throw new JsonReaderException("JsonToken {0} is not valid for closing JsonType {1}.".FormatWith(CultureInfo.InvariantCulture, endToken, currentObject)); } /// /// Sets the state based on current token type. /// protected void SetStateBasedOnCurrent() { JTokenType currentObject = Peek(); switch (currentObject) { case JTokenType.Object: _currentState = State.Object; break; case JTokenType.Array: _currentState = State.Array; break; case JTokenType.Constructor: _currentState = State.Constructor; break; case JTokenType.None: _currentState = State.Finished; break; default: throw new JsonReaderException("While setting the reader state back to current object an unexpected JsonType was encountered: " + currentObject); } } internal static bool IsStartToken(JsonToken token) { switch (token) { case JsonToken.StartObject: case JsonToken.StartArray: case JsonToken.StartConstructor: case JsonToken.PropertyName: return true; case JsonToken.None: case JsonToken.Comment: case JsonToken.Integer: case JsonToken.Float: case JsonToken.String: case JsonToken.Boolean: case JsonToken.Null: case JsonToken.Undefined: case JsonToken.EndObject: case JsonToken.EndArray: case JsonToken.EndConstructor: case JsonToken.Date: case JsonToken.Raw: case JsonToken.Bytes: return false; default: throw MiscellaneousUtils.CreateArgumentOutOfRangeException("token", token, "Unexpected JsonToken value."); } } private JTokenType GetTypeForCloseToken(JsonToken token) { switch (token) { case JsonToken.EndObject: return JTokenType.Object; case JsonToken.EndArray: return JTokenType.Array; case JsonToken.EndConstructor: return JTokenType.Constructor; default: throw new JsonReaderException("Not a valid close JsonToken: " + token); } } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// void IDisposable.Dispose() { Dispose(true); } /// /// Releases unmanaged and - optionally - managed resources /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_currentState != State.Closed && disposing) Close(); } /// /// Changes the to Closed. /// public virtual void Close() { _currentState = State.Closed; _token = JsonToken.None; _value = null; _valueType = null; } } }