JsonReader.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. // JsonKit v0.5 - A simple but flexible Json library in a single .cs file.
  2. //
  3. // Copyright (C) 2014 Topten Software (contact@toptensoftware.com) All rights reserved.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this product
  6. // except in compliance with the License. You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software distributed under the
  11. // License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  12. // either express or implied. See the License for the specific language governing permissions
  13. // and limitations under the License.
  14. using System;
  15. using System.Collections.Generic;
  16. using System.Linq;
  17. using System.IO;
  18. using System.Globalization;
  19. using System.Collections;
  20. using System.Dynamic;
  21. namespace Topten.JsonKit
  22. {
  23. /// <summary>
  24. /// Implements the IJsonReader interface
  25. /// </summary>
  26. public class JsonReader : IJsonReader
  27. {
  28. static JsonReader()
  29. {
  30. // Setup default resolvers
  31. _parserResolver = ResolveParser;
  32. _intoParserResolver = ResolveIntoParser;
  33. Func<IJsonReader, Type, object> simpleConverter = (reader, type) =>
  34. {
  35. return reader.ReadLiteral(literal => Convert.ChangeType(literal, type, CultureInfo.InvariantCulture));
  36. };
  37. Func<IJsonReader, Type, object> numberConverter = (reader, type) =>
  38. {
  39. return reader.ReadLiteralNumber(type);
  40. };
  41. // Default type handlers
  42. _parsers.Set(typeof(string), simpleConverter);
  43. _parsers.Set(typeof(char), simpleConverter);
  44. _parsers.Set(typeof(bool), simpleConverter);
  45. _parsers.Set(typeof(byte), numberConverter);
  46. _parsers.Set(typeof(sbyte), numberConverter);
  47. _parsers.Set(typeof(short), numberConverter);
  48. _parsers.Set(typeof(ushort), numberConverter);
  49. _parsers.Set(typeof(int), numberConverter);
  50. _parsers.Set(typeof(uint), numberConverter);
  51. _parsers.Set(typeof(long), numberConverter);
  52. _parsers.Set(typeof(ulong), numberConverter);
  53. _parsers.Set(typeof(decimal), numberConverter);
  54. _parsers.Set(typeof(float), numberConverter);
  55. _parsers.Set(typeof(double), numberConverter);
  56. _parsers.Set(typeof(DateTime), (reader, type) =>
  57. {
  58. return reader.ReadLiteral(literal => Utils.FromUnixMilliseconds((long)Convert.ChangeType(literal, typeof(long), CultureInfo.InvariantCulture)));
  59. });
  60. _parsers.Set(typeof(byte[]), (reader, type) =>
  61. {
  62. if (reader.CurrentToken == Token.OpenSquare)
  63. throw new CancelReaderException();
  64. return reader.ReadLiteral(literal => Convert.FromBase64String((string)Convert.ChangeType(literal, typeof(string), CultureInfo.InvariantCulture)));
  65. });
  66. _parsers.Set(typeof(Guid), (reader, type) =>
  67. {
  68. return (Guid)reader.ReadLiteral((val) =>
  69. {
  70. if (val is string str)
  71. return new Guid(str);
  72. throw new InvalidDataException("Expected string guid");
  73. });
  74. });
  75. }
  76. /// <summary>
  77. /// Constructs a new JsonReader
  78. /// </summary>
  79. /// <param name="r">The input TextReader stream</param>
  80. /// <param name="options">Options controlling parsing behaviour</param>
  81. public JsonReader(TextReader r, JsonOptions options)
  82. {
  83. _tokenizer = new Tokenizer(r, options);
  84. _options = options;
  85. }
  86. Tokenizer _tokenizer;
  87. JsonOptions _options;
  88. List<string> _contextStack = new List<string>();
  89. /// <summary>
  90. /// Gets the current parse context (ie: parent key path)
  91. /// </summary>
  92. public string Context
  93. {
  94. get
  95. {
  96. return string.Join(".", _contextStack);
  97. }
  98. }
  99. static Action<IJsonReader, object> ResolveIntoParser(Type type)
  100. {
  101. var ri = ReflectionInfo.GetReflectionInfo(type);
  102. if (ri != null)
  103. return ri.ParseInto;
  104. else
  105. return null;
  106. }
  107. static Func<IJsonReader, Type, object> ResolveParser(Type type)
  108. {
  109. // See if the Type has a static parser method - T ParseJson(IJsonReader)
  110. var parseJson = ReflectionInfo.FindParseJson(type);
  111. if (parseJson != null)
  112. {
  113. if (parseJson.GetParameters()[0].ParameterType == typeof(IJsonReader))
  114. {
  115. return (r, t) => parseJson.Invoke(null, new Object[] { r });
  116. }
  117. else
  118. {
  119. return (r, t) =>
  120. {
  121. if (r.GetLiteralKind() == LiteralKind.String)
  122. {
  123. var o = parseJson.Invoke(null, new Object[] { r.GetLiteralString() });
  124. r.NextToken();
  125. return o;
  126. }
  127. throw new InvalidDataException(string.Format("Expected string literal for type {0}", type.FullName));
  128. };
  129. }
  130. }
  131. return (r, t) =>
  132. {
  133. var into = DecoratingActivator.CreateInstance(type);
  134. r.ParseInto(into);
  135. return into;
  136. };
  137. }
  138. /// <summary>
  139. /// Gets the current position in the input stream
  140. /// </summary>
  141. public LineOffset CurrentTokenPosition
  142. {
  143. get { return _tokenizer.CurrentTokenPosition; }
  144. }
  145. /// <inheritdoc />
  146. public Token CurrentToken
  147. {
  148. get { return _tokenizer.CurrentToken; }
  149. }
  150. // ReadLiteral is implemented with a converter callback so that any
  151. // errors on converting to the target type are thrown before the tokenizer
  152. // is advanced to the next token. This ensures error location is reported
  153. // at the start of the literal, not the following token.
  154. /// <inheritdoc />
  155. public object ReadLiteral(Func<object, object> converter)
  156. {
  157. _tokenizer.Check(Token.Literal);
  158. var retv = converter(_tokenizer.LiteralValue);
  159. _tokenizer.NextToken();
  160. return retv;
  161. }
  162. /// <summary>
  163. /// Checks for EOF and throws an exception if not
  164. /// </summary>
  165. public void CheckEOF()
  166. {
  167. _tokenizer.Check(Token.EOF);
  168. }
  169. /// <inheritdoc />
  170. public object Parse(Type type)
  171. {
  172. // Null?
  173. if (_tokenizer.CurrentToken == Token.Literal && _tokenizer.LiteralKind == LiteralKind.Null)
  174. {
  175. _tokenizer.NextToken();
  176. return null;
  177. }
  178. // Handle nullable types
  179. var typeUnderlying = Nullable.GetUnderlyingType(type);
  180. if (typeUnderlying != null)
  181. type = typeUnderlying;
  182. // See if we have a reader
  183. Func<IJsonReader, Type, object> parser;
  184. if (JsonReader._parsers.TryGetValue(type, out parser))
  185. {
  186. try
  187. {
  188. return parser(this, type);
  189. }
  190. catch (CancelReaderException)
  191. {
  192. // Reader aborted trying to read this format
  193. }
  194. }
  195. // See if we have factory
  196. Func<IJsonReader, string, object> factory;
  197. if (JsonReader._typeFactories.TryGetValue(type, out factory))
  198. {
  199. // Try first without passing dictionary keys
  200. object into = factory(this, null);
  201. if (into == null)
  202. {
  203. // This is a awkward situation. The factory requires a value from the dictionary
  204. // in order to create the target object (typically an abstract class with the class
  205. // kind recorded in the Json). Since there's no guarantee of order in a json dictionary
  206. // we can't assume the required key is first.
  207. // So, create a bookmark on the tokenizer, read keys until the factory returns an
  208. // object instance and then rewind the tokenizer and continue
  209. // Create a bookmark so we can rewind
  210. _tokenizer.CreateBookmark();
  211. // Skip the opening brace
  212. _tokenizer.Skip(Token.OpenBrace);
  213. // First pass to work out type
  214. ParseDictionaryKeys(typeof(string), key =>
  215. {
  216. // Try to instantiate the object
  217. into = factory(this, (string)key);
  218. return into == null;
  219. });
  220. // Move back to start of the dictionary
  221. _tokenizer.RewindToBookmark();
  222. // Quit if still didn't get an object from the factory
  223. if (into == null)
  224. throw new InvalidOperationException("Factory didn't create object instance (probably due to a missing key in the Json)");
  225. }
  226. // Second pass
  227. ParseInto(into);
  228. // Done
  229. return into;
  230. }
  231. // Do we already have an into parser?
  232. Action<IJsonReader, object> intoParser;
  233. if (JsonReader._intoParsers.TryGetValue(type, out intoParser))
  234. {
  235. var into = DecoratingActivator.CreateInstance(type);
  236. ParseInto(into);
  237. return into;
  238. }
  239. // Enumerated type?
  240. if (type.IsEnum)
  241. {
  242. if (type.GetCustomAttributes(typeof(FlagsAttribute), false).Any())
  243. return ReadLiteral(literal => {
  244. try
  245. {
  246. if (literal is string str)
  247. return Enum.Parse(type, str);
  248. else
  249. return Enum.ToObject(type, literal);
  250. }
  251. catch
  252. {
  253. return Enum.ToObject(type, literal);
  254. }
  255. });
  256. else
  257. return ReadLiteral(literal => {
  258. try
  259. {
  260. return Enum.Parse(type, (string)literal);
  261. }
  262. catch (Exception)
  263. {
  264. var attr = type.GetCustomAttributes(typeof(JsonUnknownAttribute), false).FirstOrDefault();
  265. if (attr==null)
  266. throw;
  267. return ((JsonUnknownAttribute)attr).UnknownValue;
  268. }
  269. });
  270. }
  271. // Array?
  272. if (type.IsArray && type.GetArrayRank() == 1)
  273. {
  274. // First parse as a List<>
  275. var listType = typeof(List<>).MakeGenericType(type.GetElementType());
  276. var list = DecoratingActivator.CreateInstance(listType);
  277. ParseInto(list);
  278. return listType.GetMethod("ToArray").Invoke(list, null);
  279. }
  280. // IEnumerable
  281. if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
  282. {
  283. // First parse as a List<>
  284. var declType = type.GetGenericArguments()[0];
  285. var listType = typeof(List<>).MakeGenericType(declType);
  286. var list = DecoratingActivator.CreateInstance(listType);
  287. ParseInto(list);
  288. return list;
  289. }
  290. // Convert interfaces to concrete types
  291. if (type.IsInterface)
  292. type = Utils.ResolveInterfaceToClass(type);
  293. // Untyped dictionary?
  294. if (_tokenizer.CurrentToken == Token.OpenBrace && (type.IsAssignableFrom(typeof(IDictionary<string, object>))))
  295. {
  296. var container = (new ExpandoObject()) as IDictionary<string, object>;
  297. this.ParseDictionary(key =>
  298. {
  299. container[key] = Parse(typeof(Object));
  300. });
  301. return container;
  302. }
  303. // Untyped list?
  304. if (_tokenizer.CurrentToken == Token.OpenSquare && (type.IsAssignableFrom(typeof(List<object>))))
  305. {
  306. var container = new List<object>();
  307. ParseArray(() =>
  308. {
  309. container.Add(Parse(typeof(Object)));
  310. });
  311. return container;
  312. }
  313. // Untyped literal?
  314. if (_tokenizer.CurrentToken == Token.Literal && type.IsAssignableFrom(_tokenizer.LiteralType))
  315. {
  316. var lit = _tokenizer.LiteralValue;
  317. _tokenizer.NextToken();
  318. return lit;
  319. }
  320. // Call value type resolver
  321. if (type.IsValueType)
  322. {
  323. var tp = _parsers.Get(type, () => _parserResolver(type));
  324. if (tp != null)
  325. {
  326. return tp(this, type);
  327. }
  328. }
  329. // Call reference type resolver
  330. if (type.IsClass && type != typeof(object))
  331. {
  332. var into = DecoratingActivator.CreateInstance(type);
  333. ParseInto(into);
  334. return into;
  335. }
  336. // Give up
  337. throw new InvalidDataException(string.Format("syntax error, unexpected token {0}", _tokenizer.CurrentToken));
  338. }
  339. /// <inheritdoc />
  340. public void ParseInto(object into)
  341. {
  342. if (into == null)
  343. return;
  344. if (_tokenizer.CurrentToken == Token.Literal && _tokenizer.LiteralKind == LiteralKind.Null)
  345. {
  346. throw new InvalidOperationException("can't parse null into existing instance");
  347. //return;
  348. }
  349. var type = into.GetType();
  350. // Existing parse into handler?
  351. Action<IJsonReader,object> parseInto;
  352. if (_intoParsers.TryGetValue(type, out parseInto))
  353. {
  354. parseInto(this, into);
  355. return;
  356. }
  357. // Generic dictionary?
  358. var dictType = Utils.FindGenericInterface(type, typeof(IDictionary<,>));
  359. if (dictType!=null)
  360. {
  361. // Get the key and value types
  362. var typeKey = dictType.GetGenericArguments()[0];
  363. var typeValue = dictType.GetGenericArguments()[1];
  364. // Parse it
  365. IDictionary dict = (IDictionary)into;
  366. dict.Clear();
  367. this.ParseDictionary(typeKey, key =>
  368. {
  369. dict.Add(key, Parse(typeValue));
  370. });
  371. return;
  372. }
  373. // Generic list
  374. var listType = Utils.FindGenericInterface(type, typeof(IList<>));
  375. if (listType!=null)
  376. {
  377. // Get element type
  378. var typeElement = listType.GetGenericArguments()[0];
  379. // Parse it
  380. IList list = (IList)into;
  381. list.Clear();
  382. ParseArray(() =>
  383. {
  384. list.Add(Parse(typeElement));
  385. });
  386. return;
  387. }
  388. // Untyped dictionary
  389. var objDict = into as IDictionary;
  390. if (objDict != null)
  391. {
  392. objDict.Clear();
  393. this.ParseDictionary(key =>
  394. {
  395. objDict[key] = Parse(typeof(Object));
  396. });
  397. return;
  398. }
  399. // Untyped list
  400. var objList = into as IList;
  401. if (objList!=null)
  402. {
  403. objList.Clear();
  404. ParseArray(() =>
  405. {
  406. objList.Add(Parse(typeof(Object)));
  407. });
  408. return;
  409. }
  410. // Try to resolve a parser
  411. var intoParser = _intoParsers.Get(type, () => _intoParserResolver(type));
  412. if (intoParser != null)
  413. {
  414. intoParser(this, into);
  415. return;
  416. }
  417. throw new InvalidOperationException(string.Format("Don't know how to parse into type '{0}'", type.FullName));
  418. }
  419. /// <inheritdoc />
  420. public T Parse<T>()
  421. {
  422. return (T)Parse(typeof(T));
  423. }
  424. /// <inheritdoc />
  425. public LiteralKind GetLiteralKind()
  426. {
  427. return _tokenizer.LiteralKind;
  428. }
  429. /// <inheritdoc />
  430. public string GetLiteralString()
  431. {
  432. return _tokenizer.String;
  433. }
  434. /// <inheritdoc />
  435. public void NextToken()
  436. {
  437. _tokenizer.NextToken();
  438. }
  439. /// <inheritdoc />
  440. public void ParseDictionary(Type typeKey, Action<object> callback)
  441. {
  442. _tokenizer.Skip(Token.OpenBrace);
  443. ParseDictionaryKeys(typeKey, key => { callback(key); return true; });
  444. _tokenizer.Skip(Token.CloseBrace);
  445. }
  446. // Parse dictionary keys, calling callback for each one. Continues until end of input
  447. // or when callback returns false
  448. private void ParseDictionaryKeys(Type typeKey, Func<object, bool> callback)
  449. {
  450. // End?
  451. while (_tokenizer.CurrentToken != Token.CloseBrace)
  452. {
  453. // Parse the key
  454. object key = null;
  455. if (_tokenizer.CurrentToken == Token.Identifier && (_options & JsonOptions.StrictParser)==0)
  456. {
  457. if (typeKey != typeof(string))
  458. {
  459. throw new NotImplementedException("Identifier keys can only be used with dictionaries with string keys");
  460. }
  461. key = _tokenizer.String;
  462. _tokenizer.NextToken();
  463. }
  464. else if (_tokenizer.CurrentToken == Token.Literal)
  465. {
  466. key = Parse(typeKey);
  467. }
  468. else
  469. {
  470. throw new InvalidDataException("syntax error, expected string literal or identifier");
  471. }
  472. _tokenizer.Skip(Token.Colon);
  473. // Remember current position
  474. var pos = _tokenizer.CurrentTokenPosition;
  475. // Call the callback, quit if cancelled
  476. _contextStack.Add(key.ToString());
  477. bool doDefaultProcessing = callback(key);
  478. _contextStack.RemoveAt(_contextStack.Count-1);
  479. if (!doDefaultProcessing)
  480. return;
  481. // If the callback didn't read anything from the tokenizer, then skip it ourself
  482. if (pos.Line == _tokenizer.CurrentTokenPosition.Line && pos.Offset == _tokenizer.CurrentTokenPosition.Offset)
  483. {
  484. Parse(typeof(object));
  485. }
  486. // Separating/trailing comma
  487. if (_tokenizer.SkipIf(Token.Comma))
  488. {
  489. if ((_options & JsonOptions.StrictParser) != 0 && _tokenizer.CurrentToken == Token.CloseBrace)
  490. {
  491. throw new InvalidDataException("Trailing commas not allowed in strict mode");
  492. }
  493. continue;
  494. }
  495. // End
  496. break;
  497. }
  498. }
  499. /// <inheritdoc />
  500. public void ParseArray(Action callback)
  501. {
  502. _tokenizer.Skip(Token.OpenSquare);
  503. int index = 0;
  504. while (_tokenizer.CurrentToken != Token.CloseSquare)
  505. {
  506. _contextStack.Add(string.Format("[{0}]", index));
  507. callback();
  508. _contextStack.RemoveAt(_contextStack.Count-1);
  509. if (_tokenizer.SkipIf(Token.Comma))
  510. {
  511. if ((_options & JsonOptions.StrictParser)!=0 && _tokenizer.CurrentToken==Token.CloseSquare)
  512. {
  513. throw new InvalidDataException("Trailing commas not allowed in strict mode");
  514. }
  515. continue;
  516. }
  517. break;
  518. }
  519. _tokenizer.Skip(Token.CloseSquare);
  520. }
  521. // Yikes!
  522. internal static Func<Type, Action<IJsonReader, object>> _intoParserResolver;
  523. internal static Func<Type, Func<IJsonReader, Type, object>> _parserResolver;
  524. internal static ThreadSafeCache<Type, Func<IJsonReader, Type, object>> _parsers = new ThreadSafeCache<Type, Func<IJsonReader, Type, object>>();
  525. internal static ThreadSafeCache<Type, Action<IJsonReader, object>> _intoParsers = new ThreadSafeCache<Type, Action<IJsonReader, object>>();
  526. internal static ThreadSafeCache<Type, Func<IJsonReader, string, object>> _typeFactories = new ThreadSafeCache<Type, Func<IJsonReader, string, object>>();
  527. }
  528. }