1 #region License
2 // Copyright (c) 2007 James Newton-King
3 //
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
11 // conditions:
12 //
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
15 //
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.
24 #endregion
25 
26 using System;
27 using Newtonsoft.Json.Utilities;
28 
29 namespace Newtonsoft.Json.Linq
30 {
31   /// <summary>
32   /// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data.
33   /// </summary>
34   public class JTokenReader : JsonReader, IJsonLineInfo
35   {
36     private readonly JToken _root;
37     private JToken _parent;
38     private JToken _current;
39 
40     /// <summary>
41     /// Initializes a new instance of the <see cref="JTokenReader"/> class.
42     /// </summary>
43     /// <param name="token">The token to read from.</param>
JTokenReader(JToken token)44     public JTokenReader(JToken token)
45     {
46       ValidationUtils.ArgumentNotNull(token, "token");
47 
48       _root = token;
49       _current = token;
50     }
51 
52     /// <summary>
53     /// Reads the next JSON token from the stream as a <see cref="T:Byte[]"/>.
54     /// </summary>
55     /// <returns>
56     /// A <see cref="T:Byte[]"/> or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.
57     /// </returns>
ReadAsBytes()58     public override byte[] ReadAsBytes()
59     {
60       return ReadAsBytesInternal();
61     }
62 
63     /// <summary>
64     /// Reads the next JSON token from the stream as a <see cref="Nullable{Decimal}"/>.
65     /// </summary>
66     /// <returns>A <see cref="Nullable{Decimal}"/>. This method will return <c>null</c> at the end of an array.</returns>
ReadAsDecimal()67     public override decimal? ReadAsDecimal()
68     {
69       return ReadAsDecimalInternal();
70     }
71 
72     /// <summary>
73     /// Reads the next JSON token from the stream as a <see cref="Nullable{Int32}"/>.
74     /// </summary>
75     /// <returns>A <see cref="Nullable{Int32}"/>. This method will return <c>null</c> at the end of an array.</returns>
ReadAsInt32()76     public override int? ReadAsInt32()
77     {
78       return ReadAsInt32Internal();
79     }
80 
81     /// <summary>
82     /// Reads the next JSON token from the stream as a <see cref="String"/>.
83     /// </summary>
84     /// <returns>A <see cref="String"/>. This method will return <c>null</c> at the end of an array.</returns>
ReadAsString()85     public override string ReadAsString()
86     {
87       return ReadAsStringInternal();
88     }
89 
90     /// <summary>
91     /// Reads the next JSON token from the stream as a <see cref="Nullable{DateTime}"/>.
92     /// </summary>
93     /// <returns>A <see cref="String"/>. This method will return <c>null</c> at the end of an array.</returns>
ReadAsDateTime()94     public override DateTime? ReadAsDateTime()
95     {
96       return ReadAsDateTimeInternal();
97     }
98 
99 #if !NET20
100     /// <summary>
101     /// Reads the next JSON token from the stream as a <see cref="Nullable{DateTimeOffset}"/>.
102     /// </summary>
103     /// <returns>A <see cref="Nullable{DateTimeOffset}"/>. This method will return <c>null</c> at the end of an array.</returns>
ReadAsDateTimeOffset()104     public override DateTimeOffset? ReadAsDateTimeOffset()
105     {
106       return ReadAsDateTimeOffsetInternal();
107     }
108 #endif
109 
ReadInternal()110     internal override bool ReadInternal()
111     {
112       if (CurrentState != State.Start)
113       {
114         JContainer container = _current as JContainer;
115         if (container != null && _parent != container)
116           return ReadInto(container);
117         else
118           return ReadOver(_current);
119       }
120 
121       SetToken(_current);
122       return true;
123     }
124 
125     /// <summary>
126     /// Reads the next JSON token from the stream.
127     /// </summary>
128     /// <returns>
129     /// true if the next token was read successfully; false if there are no more tokens to read.
130     /// </returns>
Read()131     public override bool Read()
132     {
133       _readType = ReadType.Read;
134 
135       return ReadInternal();
136     }
137 
ReadOver(JToken t)138     private bool ReadOver(JToken t)
139     {
140       if (t == _root)
141         return ReadToEnd();
142 
143       JToken next = t.Next;
144       if ((next == null || next == t) || t == t.Parent.Last)
145       {
146         if (t.Parent == null)
147           return ReadToEnd();
148 
149         return SetEnd(t.Parent);
150       }
151       else
152       {
153         _current = next;
154         SetToken(_current);
155         return true;
156       }
157     }
158 
ReadToEnd()159     private bool ReadToEnd()
160     {
161       SetToken(JsonToken.None);
162       return false;
163     }
164 
165     private bool IsEndElement
166     {
167       get { return (_current == _parent); }
168     }
169 
GetEndToken(JContainer c)170     private JsonToken? GetEndToken(JContainer c)
171     {
172       switch (c.Type)
173       {
174         case JTokenType.Object:
175           return JsonToken.EndObject;
176         case JTokenType.Array:
177           return JsonToken.EndArray;
178         case JTokenType.Constructor:
179           return JsonToken.EndConstructor;
180         case JTokenType.Property:
181           return null;
182         default:
183           throw MiscellaneousUtils.CreateArgumentOutOfRangeException("Type", c.Type, "Unexpected JContainer type.");
184       }
185     }
186 
ReadInto(JContainer c)187     private bool ReadInto(JContainer c)
188     {
189       JToken firstChild = c.First;
190       if (firstChild == null)
191       {
192         return SetEnd(c);
193       }
194       else
195       {
196         SetToken(firstChild);
197         _current = firstChild;
198         _parent = c;
199         return true;
200       }
201     }
202 
SetEnd(JContainer c)203     private bool SetEnd(JContainer c)
204     {
205       JsonToken? endToken = GetEndToken(c);
206       if (endToken != null)
207       {
208         SetToken(endToken.Value);
209         _current = c;
210         _parent = c;
211         return true;
212       }
213       else
214       {
215         return ReadOver(c);
216       }
217     }
218 
SetToken(JToken token)219     private void SetToken(JToken token)
220     {
221       switch (token.Type)
222       {
223         case JTokenType.Object:
224           SetToken(JsonToken.StartObject);
225           break;
226         case JTokenType.Array:
227           SetToken(JsonToken.StartArray);
228           break;
229         case JTokenType.Constructor:
230           SetToken(JsonToken.StartConstructor);
231           break;
232         case JTokenType.Property:
233           SetToken(JsonToken.PropertyName, ((JProperty)token).Name);
234           break;
235         case JTokenType.Comment:
236           SetToken(JsonToken.Comment, ((JValue)token).Value);
237           break;
238         case JTokenType.Integer:
239           SetToken(JsonToken.Integer, ((JValue)token).Value);
240           break;
241         case JTokenType.Float:
242           SetToken(JsonToken.Float, ((JValue)token).Value);
243           break;
244         case JTokenType.String:
245           SetToken(JsonToken.String, ((JValue)token).Value);
246           break;
247         case JTokenType.Boolean:
248           SetToken(JsonToken.Boolean, ((JValue)token).Value);
249           break;
250         case JTokenType.Null:
251           SetToken(JsonToken.Null, ((JValue)token).Value);
252           break;
253         case JTokenType.Undefined:
254           SetToken(JsonToken.Undefined, ((JValue)token).Value);
255           break;
256         case JTokenType.Date:
257           SetToken(JsonToken.Date, ((JValue)token).Value);
258           break;
259         case JTokenType.Raw:
260           SetToken(JsonToken.Raw, ((JValue)token).Value);
261           break;
262         case JTokenType.Bytes:
263           SetToken(JsonToken.Bytes, ((JValue)token).Value);
264           break;
265         case JTokenType.Guid:
266           SetToken(JsonToken.String, SafeToString(((JValue)token).Value));
267           break;
268         case JTokenType.Uri:
269           SetToken(JsonToken.String, SafeToString(((JValue)token).Value));
270           break;
271         case JTokenType.TimeSpan:
272           SetToken(JsonToken.String, SafeToString(((JValue)token).Value));
273           break;
274         default:
275           throw MiscellaneousUtils.CreateArgumentOutOfRangeException("Type", token.Type, "Unexpected JTokenType.");
276       }
277     }
278 
SafeToString(object value)279     private string SafeToString(object value)
280     {
281       return (value != null) ? value.ToString() : null;
282     }
283 
IJsonLineInfo.HasLineInfo()284     bool IJsonLineInfo.HasLineInfo()
285     {
286       if (CurrentState == State.Start)
287         return false;
288 
289       IJsonLineInfo info = IsEndElement ? null : _current;
290       return (info != null && info.HasLineInfo());
291     }
292 
293     int IJsonLineInfo.LineNumber
294     {
295       get
296       {
297         if (CurrentState == State.Start)
298           return 0;
299 
300         IJsonLineInfo info = IsEndElement ? null : _current;
301         if (info != null)
302           return info.LineNumber;
303 
304         return 0;
305       }
306     }
307 
308     int IJsonLineInfo.LinePosition
309     {
310       get
311       {
312         if (CurrentState == State.Start)
313           return 0;
314 
315         IJsonLineInfo info = IsEndElement ? null : _current;
316         if (info != null)
317           return info.LinePosition;
318 
319         return 0;
320       }
321     }
322   }
323 }