JsonReader.cs 20 KB

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