2 // Copyright (c) 2007 James Newton-King
4 // Permission is hereby granted, free of charge, to any person
5 // obtaining a copy of this software and associated documentation
6 // files (the "Software"), to deal in the Software without
7 // restriction, including without limitation the rights to use,
8 // copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the
10 // Software is furnished to do so, subject to the following
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 // OTHER DEALINGS IN THE SOFTWARE.
27 using System.Collections.Generic;
29 using System.Globalization;
30 using Newtonsoft.Json.Linq;
31 using Newtonsoft.Json.Utilities;
33 namespace Newtonsoft.Json
36 /// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data.
38 public abstract class JsonReader : IDisposable
41 /// Specifies the state of the reader.
46 /// The Read method has not been called.
50 /// The end of the file has been reached successfully.
54 /// Reader is at a property.
58 /// Reader is at the start of an object.
62 /// Reader is in an object.
66 /// Reader is at the start of an array.
70 /// Reader is in an array.
74 /// The Close method has been called.
78 /// Reader has just read a value.
82 /// Reader is at the start of a constructor.
86 /// Reader in a constructor.
90 /// An error occurred that prevents the read operation from continuing.
94 /// The end of the file has been reached successfully.
100 private JsonToken _token;
101 private object _value;
102 private Type _valueType;
103 private char _quoteChar;
104 private State _currentState;
105 private JTokenType _currentTypeContext;
108 /// Gets the current reader state.
110 /// <value>The current reader state.</value>
111 protected State CurrentState
113 get { return _currentState; }
118 private readonly List<JTokenType> _stack;
121 /// Gets or sets a value indicating whether the underlying stream or
122 /// <see cref="TextReader"/> should be closed when the reader is closed.
125 /// true to close the underlying stream or <see cref="TextReader"/> when
126 /// the reader is closed; otherwise false. The default is true.
128 public bool CloseInput { get; set; }
131 /// Gets the quotation mark character used to enclose the value of a string.
133 public virtual char QuoteChar
135 get { return _quoteChar; }
136 protected internal set { _quoteChar = value; }
140 /// Gets the type of the current Json token.
142 public virtual JsonToken TokenType
144 get { return _token; }
148 /// Gets the text value of the current Json token.
150 public virtual object Value
152 get { return _value; }
156 /// Gets The Common Language Runtime (CLR) type for the current Json token.
158 public virtual Type ValueType
160 get { return _valueType; }
164 /// Gets the depth of the current token in the JSON document.
166 /// <value>The depth of the current token in the JSON document.</value>
167 public virtual int Depth
171 int depth = _top - 1;
172 if (IsStartToken(TokenType))
180 /// Initializes a new instance of the <see cref="JsonReader"/> class with the specified <see cref="TextReader"/>.
182 protected JsonReader()
184 _currentState = State.Start;
185 _stack = new List<JTokenType>();
189 Push(JTokenType.None);
192 private void Push(JTokenType value)
196 _currentTypeContext = value;
199 private JTokenType Pop()
201 JTokenType value = Peek();
202 _stack.RemoveAt(_stack.Count - 1);
204 _currentTypeContext = _stack[_top - 1];
209 private JTokenType Peek()
211 return _currentTypeContext;
215 /// Reads the next JSON token from the stream.
217 /// <returns>true if the next token was read successfully; false if there are no more tokens to read.</returns>
218 public abstract bool Read();
221 /// Reads the next JSON token from the stream as a <see cref="T:Byte[]"/>.
223 /// <returns>A <see cref="T:Byte[]"/> or a null reference if the next JSON token is null.</returns>
224 public abstract byte[] ReadAsBytes();
227 /// Reads the next JSON token from the stream as a <see cref="Nullable{Decimal}"/>.
229 /// <returns>A <see cref="Nullable{Decimal}"/>.</returns>
230 public abstract decimal? ReadAsDecimal();
234 /// Reads the next JSON token from the stream as a <see cref="Nullable{DateTimeOffset}"/>.
236 /// <returns>A <see cref="Nullable{DateTimeOffset}"/>.</returns>
237 public abstract DateTimeOffset? ReadAsDateTimeOffset();
241 /// Skips the children of the current token.
245 if (IsStartToken(TokenType))
249 while (Read() && (depth < Depth))
256 /// Sets the current token.
258 /// <param name="newToken">The new token.</param>
259 protected void SetToken(JsonToken newToken)
261 SetToken(newToken, null);
265 /// Sets the current token and value.
267 /// <param name="newToken">The new token.</param>
268 /// <param name="value">The value.</param>
269 protected virtual void SetToken(JsonToken newToken, object value)
275 case JsonToken.StartObject:
276 _currentState = State.ObjectStart;
277 Push(JTokenType.Object);
279 case JsonToken.StartArray:
280 _currentState = State.ArrayStart;
281 Push(JTokenType.Array);
283 case JsonToken.StartConstructor:
284 _currentState = State.ConstructorStart;
285 Push(JTokenType.Constructor);
287 case JsonToken.EndObject:
288 ValidateEnd(JsonToken.EndObject);
289 _currentState = State.PostValue;
291 case JsonToken.EndArray:
292 ValidateEnd(JsonToken.EndArray);
293 _currentState = State.PostValue;
295 case JsonToken.EndConstructor:
296 ValidateEnd(JsonToken.EndConstructor);
297 _currentState = State.PostValue;
299 case JsonToken.PropertyName:
300 _currentState = State.Property;
301 Push(JTokenType.Property);
303 case JsonToken.Undefined:
304 case JsonToken.Integer:
305 case JsonToken.Float:
306 case JsonToken.Boolean:
309 case JsonToken.String:
311 case JsonToken.Bytes:
312 _currentState = State.PostValue;
316 JTokenType current = Peek();
317 if (current == JTokenType.Property && _currentState == State.PostValue)
323 _valueType = value.GetType();
332 private void ValidateEnd(JsonToken endToken)
334 JTokenType currentObject = Pop();
336 if (GetTypeForCloseToken(endToken) != currentObject)
337 throw new JsonReaderException("JsonToken {0} is not valid for closing JsonType {1}.".FormatWith(CultureInfo.InvariantCulture, endToken, currentObject));
341 /// Sets the state based on current token type.
343 protected void SetStateBasedOnCurrent()
345 JTokenType currentObject = Peek();
347 switch (currentObject)
349 case JTokenType.Object:
350 _currentState = State.Object;
352 case JTokenType.Array:
353 _currentState = State.Array;
355 case JTokenType.Constructor:
356 _currentState = State.Constructor;
358 case JTokenType.None:
359 _currentState = State.Finished;
362 throw new JsonReaderException("While setting the reader state back to current object an unexpected JsonType was encountered: {0}".FormatWith(CultureInfo.InvariantCulture, currentObject));
366 internal static bool IsPrimitiveToken(JsonToken token)
370 case JsonToken.Integer:
371 case JsonToken.Float:
372 case JsonToken.String:
373 case JsonToken.Boolean:
374 case JsonToken.Undefined:
377 case JsonToken.Bytes:
384 internal static bool IsStartToken(JsonToken token)
388 case JsonToken.StartObject:
389 case JsonToken.StartArray:
390 case JsonToken.StartConstructor:
391 case JsonToken.PropertyName:
394 case JsonToken.Comment:
395 case JsonToken.Integer:
396 case JsonToken.Float:
397 case JsonToken.String:
398 case JsonToken.Boolean:
400 case JsonToken.Undefined:
401 case JsonToken.EndObject:
402 case JsonToken.EndArray:
403 case JsonToken.EndConstructor:
406 case JsonToken.Bytes:
409 throw MiscellaneousUtils.CreateArgumentOutOfRangeException("token", token, "Unexpected JsonToken value.");
413 private JTokenType GetTypeForCloseToken(JsonToken token)
417 case JsonToken.EndObject:
418 return JTokenType.Object;
419 case JsonToken.EndArray:
420 return JTokenType.Array;
421 case JsonToken.EndConstructor:
422 return JTokenType.Constructor;
424 throw new JsonReaderException("Not a valid close JsonToken: {0}".FormatWith(CultureInfo.InvariantCulture, token));
429 /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
431 void IDisposable.Dispose()
437 /// Releases unmanaged and - optionally - managed resources
439 /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
440 protected virtual void Dispose(bool disposing)
442 if (_currentState != State.Closed && disposing)
447 /// Changes the <see cref="State"/> to Closed.
449 public virtual void Close()
451 _currentState = State.Closed;
452 _token = JsonToken.None;