You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

325 lines
10 KiB

  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. using System;
  26. using Newtonsoft.Json.Utilities;
  27. namespace Newtonsoft.Json.Linq
  28. {
  29. /// <summary>
  30. /// Represents a reader that provides fast, non-cached, forward-only access to serialized JSON data.
  31. /// </summary>
  32. internal class JTokenReader : JsonReader, IJsonLineInfo
  33. {
  34. private readonly JToken _root;
  35. private string _initialPath;
  36. private JToken _parent;
  37. private JToken _current;
  38. /// <summary>
  39. /// Gets the <see cref="JToken"/> at the reader's current position.
  40. /// </summary>
  41. public JToken CurrentToken => _current;
  42. /// <summary>
  43. /// Initializes a new instance of the <see cref="JTokenReader"/> class.
  44. /// </summary>
  45. /// <param name="token">The token to read from.</param>
  46. public JTokenReader(JToken token)
  47. {
  48. ValidationUtils.ArgumentNotNull(token, nameof(token));
  49. _root = token;
  50. }
  51. // this is used by json.net schema
  52. internal JTokenReader(JToken token, string initialPath)
  53. : this(token)
  54. {
  55. _initialPath = initialPath;
  56. }
  57. /// <summary>
  58. /// Reads the next JSON token from the underlying <see cref="JToken"/>.
  59. /// </summary>
  60. /// <returns>
  61. /// <c>true</c> if the next token was read successfully; <c>false</c> if there are no more tokens to read.
  62. /// </returns>
  63. public override bool Read()
  64. {
  65. if (CurrentState != State.Start)
  66. {
  67. if (_current == null)
  68. {
  69. return false;
  70. }
  71. if (_current is JContainer container && _parent != container)
  72. {
  73. return ReadInto(container);
  74. }
  75. else
  76. {
  77. return ReadOver(_current);
  78. }
  79. }
  80. _current = _root;
  81. SetToken(_current);
  82. return true;
  83. }
  84. private bool ReadOver(JToken t)
  85. {
  86. if (t == _root)
  87. {
  88. return ReadToEnd();
  89. }
  90. JToken next = t.Next;
  91. if ((next == null || next == t) || t == t.Parent.Last)
  92. {
  93. if (t.Parent == null)
  94. {
  95. return ReadToEnd();
  96. }
  97. return SetEnd(t.Parent);
  98. }
  99. else
  100. {
  101. _current = next;
  102. SetToken(_current);
  103. return true;
  104. }
  105. }
  106. private bool ReadToEnd()
  107. {
  108. _current = null;
  109. SetToken(JsonToken.None);
  110. return false;
  111. }
  112. private JsonToken? GetEndToken(JContainer c)
  113. {
  114. switch (c.Type)
  115. {
  116. case JTokenType.Object:
  117. return JsonToken.EndObject;
  118. case JTokenType.Array:
  119. return JsonToken.EndArray;
  120. case JTokenType.Constructor:
  121. return JsonToken.EndConstructor;
  122. case JTokenType.Property:
  123. return null;
  124. default:
  125. throw MiscellaneousUtils.CreateArgumentOutOfRangeException(nameof(c.Type), c.Type, "Unexpected JContainer type.");
  126. }
  127. }
  128. private bool ReadInto(JContainer c)
  129. {
  130. JToken firstChild = c.First;
  131. if (firstChild == null)
  132. {
  133. return SetEnd(c);
  134. }
  135. else
  136. {
  137. SetToken(firstChild);
  138. _current = firstChild;
  139. _parent = c;
  140. return true;
  141. }
  142. }
  143. private bool SetEnd(JContainer c)
  144. {
  145. JsonToken? endToken = GetEndToken(c);
  146. if (endToken != null)
  147. {
  148. SetToken(endToken.GetValueOrDefault());
  149. _current = c;
  150. _parent = c;
  151. return true;
  152. }
  153. else
  154. {
  155. return ReadOver(c);
  156. }
  157. }
  158. private void SetToken(JToken token)
  159. {
  160. switch (token.Type)
  161. {
  162. case JTokenType.Object:
  163. SetToken(JsonToken.StartObject);
  164. break;
  165. case JTokenType.Array:
  166. SetToken(JsonToken.StartArray);
  167. break;
  168. case JTokenType.Constructor:
  169. SetToken(JsonToken.StartConstructor, ((JConstructor)token).Name);
  170. break;
  171. case JTokenType.Property:
  172. SetToken(JsonToken.PropertyName, ((JProperty)token).Name);
  173. break;
  174. case JTokenType.Comment:
  175. SetToken(JsonToken.Comment, ((JValue)token).Value);
  176. break;
  177. case JTokenType.Integer:
  178. SetToken(JsonToken.Integer, ((JValue)token).Value);
  179. break;
  180. case JTokenType.Float:
  181. SetToken(JsonToken.Float, ((JValue)token).Value);
  182. break;
  183. case JTokenType.String:
  184. SetToken(JsonToken.String, ((JValue)token).Value);
  185. break;
  186. case JTokenType.Boolean:
  187. SetToken(JsonToken.Boolean, ((JValue)token).Value);
  188. break;
  189. case JTokenType.Null:
  190. SetToken(JsonToken.Null, ((JValue)token).Value);
  191. break;
  192. case JTokenType.Undefined:
  193. SetToken(JsonToken.Undefined, ((JValue)token).Value);
  194. break;
  195. case JTokenType.Date:
  196. SetToken(JsonToken.Date, ((JValue)token).Value);
  197. break;
  198. case JTokenType.Raw:
  199. SetToken(JsonToken.Raw, ((JValue)token).Value);
  200. break;
  201. case JTokenType.Bytes:
  202. SetToken(JsonToken.Bytes, ((JValue)token).Value);
  203. break;
  204. case JTokenType.Guid:
  205. SetToken(JsonToken.String, SafeToString(((JValue)token).Value));
  206. break;
  207. case JTokenType.Uri:
  208. object v = ((JValue)token).Value;
  209. Uri uri = v as Uri;
  210. SetToken(JsonToken.String, uri != null ? uri.OriginalString : SafeToString(v));
  211. break;
  212. case JTokenType.TimeSpan:
  213. SetToken(JsonToken.String, SafeToString(((JValue)token).Value));
  214. break;
  215. default:
  216. throw MiscellaneousUtils.CreateArgumentOutOfRangeException(nameof(token.Type), token.Type, "Unexpected JTokenType.");
  217. }
  218. }
  219. private string SafeToString(object value)
  220. {
  221. return value?.ToString();
  222. }
  223. bool IJsonLineInfo.HasLineInfo()
  224. {
  225. if (CurrentState == State.Start)
  226. {
  227. return false;
  228. }
  229. IJsonLineInfo info = _current;
  230. return (info != null && info.HasLineInfo());
  231. }
  232. int IJsonLineInfo.LineNumber
  233. {
  234. get
  235. {
  236. if (CurrentState == State.Start)
  237. {
  238. return 0;
  239. }
  240. IJsonLineInfo info = _current;
  241. if (info != null)
  242. {
  243. return info.LineNumber;
  244. }
  245. return 0;
  246. }
  247. }
  248. int IJsonLineInfo.LinePosition
  249. {
  250. get
  251. {
  252. if (CurrentState == State.Start)
  253. {
  254. return 0;
  255. }
  256. IJsonLineInfo info = _current;
  257. if (info != null)
  258. {
  259. return info.LinePosition;
  260. }
  261. return 0;
  262. }
  263. }
  264. /// <summary>
  265. /// Gets the path of the current JSON token.
  266. /// </summary>
  267. public override string Path
  268. {
  269. get
  270. {
  271. string path = base.Path;
  272. if (_initialPath == null)
  273. {
  274. _initialPath = _root.Path;
  275. }
  276. if (!string.IsNullOrEmpty(_initialPath))
  277. {
  278. if (string.IsNullOrEmpty(path))
  279. {
  280. return _initialPath;
  281. }
  282. if (path.StartsWith('['))
  283. {
  284. path = _initialPath + path;
  285. }
  286. else
  287. {
  288. path = _initialPath + "." + path;
  289. }
  290. }
  291. return path;
  292. }
  293. }
  294. }
  295. }