JsonKit.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  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.IO;
  16. namespace Topten.JsonKit
  17. {
  18. /// <summary>
  19. /// The main static interface class to JsonKit
  20. /// </summary>
  21. public static class Json
  22. {
  23. static Json()
  24. {
  25. WriteWhitespaceDefault = true;
  26. StrictParserDefault = false;
  27. #if !JSONKIT_NO_EMIT
  28. Json.SetFormatterResolver(Emit.MakeFormatter);
  29. Json.SetParserResolver(Emit.MakeParser);
  30. Json.SetIntoParserResolver(Emit.MakeIntoParser);
  31. #endif
  32. }
  33. /// <summary>
  34. /// Controls whether the write whitespace by default
  35. /// </summary>
  36. public static bool WriteWhitespaceDefault
  37. {
  38. get;
  39. set;
  40. }
  41. /// <summary>
  42. /// Controls whether parsing should be strict by default
  43. /// </summary>
  44. public static bool StrictParserDefault
  45. {
  46. get;
  47. set;
  48. }
  49. // Write an object to a text writer
  50. /// <summary>
  51. /// Writes an object to a TextWriter
  52. /// </summary>
  53. /// <param name="w">The target text writer</param>
  54. /// <param name="o">The object to be written</param>
  55. /// <param name="options">Options controlling output formatting</param>
  56. public static void Write(TextWriter w, object o, JsonOptions options = JsonOptions.None)
  57. {
  58. var writer = new JsonWriter(w, ResolveOptions(options));
  59. writer.WriteValue(o);
  60. }
  61. /// <summary>
  62. /// Controls whether previous version should be saved if AutoSavePreviousVersion is used
  63. /// </summary>
  64. public static bool SavePreviousVersions
  65. {
  66. get;
  67. set;
  68. }
  69. /// <summary>
  70. /// Write a file atomically by writing to a temp file and then renaming it - prevents corrupted files if crash
  71. /// in middle of writing file.
  72. /// </summary>
  73. /// <param name="filename">The output file name</param>
  74. /// <param name="o">The object to be written</param>
  75. /// <param name="options">Options controlling output</param>
  76. /// <param name="backupFilename">An optional back filename where previous version will be written</param>
  77. public static void WriteFileAtomic(string filename, object o, JsonOptions options = JsonOptions.None, string backupFilename = null)
  78. {
  79. var tempName = filename + ".tmp";
  80. try
  81. {
  82. // Write the temp file
  83. WriteFile(tempName, o, (options | JsonOptions.Flush));
  84. if (System.IO.File.Exists(filename))
  85. {
  86. bool savePreviousVersion = false;
  87. if ((options & JsonOptions.AutoSavePreviousVersion)!=0)
  88. {
  89. savePreviousVersion = SavePreviousVersions;
  90. }
  91. else if ((options & JsonOptions.SavePreviousVersion)!=0)
  92. {
  93. savePreviousVersion = true;
  94. }
  95. // Work out backup filename
  96. if (savePreviousVersion)
  97. {
  98. // Make sure have a backup filename
  99. if (backupFilename == null)
  100. {
  101. backupFilename = filename + ".previous";
  102. }
  103. }
  104. else
  105. {
  106. // No backup
  107. backupFilename = null;
  108. }
  109. // Replace it
  110. int retry = 0;
  111. while (true)
  112. {
  113. try
  114. {
  115. File.Replace(tempName, filename, backupFilename);
  116. break;
  117. }
  118. catch (System.IO.IOException x)
  119. {
  120. retry++;
  121. if (retry >= 5)
  122. {
  123. throw new System.IO.IOException(string.Format("Failed to replace temp file {0} with {1} and backup {2}, reason {3}", tempName, filename, backupFilename, x.Message), x);
  124. }
  125. System.Threading.Thread.Sleep(2000);
  126. }
  127. }
  128. }
  129. else
  130. {
  131. // Rename it
  132. File.Move(tempName, filename);
  133. }
  134. }
  135. catch
  136. {
  137. Utils.DeleteFile(tempName);
  138. throw;
  139. }
  140. }
  141. /// <summary>
  142. /// Write an object to a file
  143. /// </summary>
  144. /// <param name="filename">The output filename</param>
  145. /// <param name="o">The object to be written</param>
  146. /// <param name="options">Options controlling output</param>
  147. public static void WriteFile(string filename, object o, JsonOptions options = JsonOptions.None)
  148. {
  149. using (var w = new StreamWriter(filename))
  150. {
  151. Write(w, o, options);
  152. if ((options & JsonOptions.Flush) != 0)
  153. {
  154. w.Flush();
  155. w.BaseStream.Flush();
  156. }
  157. }
  158. }
  159. /// <summary>
  160. /// Format an object as a json string
  161. /// </summary>
  162. /// <param name="o">The value to be formatted</param>
  163. /// <param name="options">Options controlling output</param>
  164. /// <returns>The formatted string</returns>
  165. public static string Format(object o, JsonOptions options = JsonOptions.None)
  166. {
  167. var sw = new StringWriter();
  168. var writer = new JsonWriter(sw, ResolveOptions(options));
  169. writer.WriteValue(o);
  170. return sw.ToString();
  171. }
  172. /// <summary>
  173. /// Parse an object of specified type from a text reader
  174. /// </summary>
  175. /// <param name="r">The text reader to read from</param>
  176. /// <param name="type">The type of object to be parsed</param>
  177. /// <param name="options">Options controlling parsing</param>
  178. /// <returns>The parsed object</returns>
  179. public static object Parse(TextReader r, Type type, JsonOptions options = JsonOptions.None)
  180. {
  181. JsonReader reader = null;
  182. try
  183. {
  184. reader = new JsonReader(r, ResolveOptions(options));
  185. var retv = reader.Parse(type);
  186. reader.CheckEOF();
  187. return retv;
  188. }
  189. catch (Exception x)
  190. {
  191. var loc = reader == null ? new LineOffset() : reader.CurrentTokenPosition;
  192. Console.WriteLine("Exception thrown while parsing JSON at {0}, context:{1}\n{2}", loc, reader?.Context, x.ToString());
  193. throw new JsonParseException(x, reader?.Context, loc);
  194. }
  195. }
  196. /// <summary>
  197. /// Parse an object of specified type from a text reader
  198. /// </summary>
  199. /// <typeparam name="T">The type of object to be parsed</typeparam>
  200. /// <param name="r">The text reader to read from</param>
  201. /// <param name="options">Options controlling parsing</param>
  202. /// <returns>The parsed object</returns>
  203. public static T Parse<T>(TextReader r, JsonOptions options = JsonOptions.None)
  204. {
  205. return (T)Parse(r, typeof(T), options);
  206. }
  207. /// <summary>
  208. /// Parse from text reader into an already instantied object
  209. /// </summary>
  210. /// <param name="r">The text reader to read from </param>
  211. /// <param name="into">The object to serialize into</param>
  212. /// <param name="options">Options controlling parsing</param>
  213. public static void ParseInto(TextReader r, Object into, JsonOptions options = JsonOptions.None)
  214. {
  215. if (into == null)
  216. throw new NullReferenceException();
  217. if (into.GetType().IsValueType)
  218. throw new InvalidOperationException("Can't ParseInto a value type");
  219. JsonReader reader = null;
  220. try
  221. {
  222. reader = new JsonReader(r, ResolveOptions(options));
  223. reader.ParseInto(into);
  224. reader.CheckEOF();
  225. }
  226. catch (Exception x)
  227. {
  228. var loc = reader == null ? new LineOffset() : reader.CurrentTokenPosition;
  229. Console.WriteLine("Exception thrown while parsing JSON at {0}, context:{1}\n{2}", loc, reader.Context, x.ToString());
  230. throw new JsonParseException(x,reader.Context,loc);
  231. }
  232. }
  233. /// <summary>
  234. /// Parse an object of specified type from a file
  235. /// </summary>
  236. /// <param name="filename">The input filename</param>
  237. /// <param name="type">The type of object to be parsed</param>
  238. /// <param name="options">Options controlling parsing</param>
  239. /// <returns>The parsed object instance</returns>
  240. public static object ParseFile(string filename, Type type, JsonOptions options = JsonOptions.None)
  241. {
  242. using (var r = new StreamReader(filename))
  243. {
  244. return Parse(r, type, options);
  245. }
  246. }
  247. // Parse an object of specified type from a file
  248. /// <summary>
  249. /// Parse an object of specified type from a file
  250. /// </summary>
  251. /// <typeparam name="T">The type of object to be parsed</typeparam>
  252. /// <param name="filename">The input filename</param>
  253. /// <param name="options">Options controlling parsing</param>
  254. /// <returns></returns>
  255. public static T ParseFile<T>(string filename, JsonOptions options = JsonOptions.None)
  256. {
  257. using (var r = new StreamReader(filename))
  258. {
  259. return Parse<T>(r, options);
  260. }
  261. }
  262. /// <summary>
  263. /// Parse from file into an already instantied object
  264. /// </summary>
  265. /// <param name="filename">The input filename</param>
  266. /// <param name="into">The object to serialize into</param>
  267. /// <param name="options">Options controlling parsing</param>
  268. public static void ParseFileInto(string filename, Object into, JsonOptions options = JsonOptions.None)
  269. {
  270. using (var r = new StreamReader(filename))
  271. {
  272. ParseInto(r, into, options);
  273. }
  274. }
  275. /// <summary>
  276. /// Parse an object from a string
  277. /// </summary>
  278. /// <param name="data">The JSON data</param>
  279. /// <param name="type">The type of object to be parsed</param>
  280. /// <param name="options">Options controlling parsing</param>
  281. /// <returns>The parsed object instance</returns>
  282. public static object Parse(string data, Type type, JsonOptions options = JsonOptions.None)
  283. {
  284. return Parse(new StringReader(data), type, options);
  285. }
  286. /// <summary>
  287. /// Parse an object from a string
  288. /// </summary>
  289. /// <typeparam name="T">The type of object to be parsed</typeparam>
  290. /// <param name="data">The JSON data</param>
  291. /// <param name="options">Options controlling parsing</param>
  292. /// <returns></returns>
  293. public static T Parse<T>(string data, JsonOptions options = JsonOptions.None)
  294. {
  295. return (T)Parse<T>(new StringReader(data), options);
  296. }
  297. /// <summary>
  298. /// Parse from string into an already instantiated object
  299. /// </summary>
  300. /// <param name="data">The JSON data</param>
  301. /// <param name="into">The object to serialize into</param>
  302. /// <param name="options">Options controlling parsing</param>
  303. public static void ParseInto(string data, Object into, JsonOptions options = JsonOptions.None)
  304. {
  305. ParseInto(new StringReader(data), into, options);
  306. }
  307. /// <summary>
  308. /// Create a clone of an object by serializing to JSON and the deserializing into a new instance
  309. /// </summary>
  310. /// <typeparam name="T">The type of object to be cloned</typeparam>
  311. /// <param name="source">The object to be cloned</param>
  312. /// <returns>A cloned instance</returns>
  313. public static T Clone<T>(T source)
  314. {
  315. return (T)Reparse(source.GetType(), source);
  316. }
  317. // Create a clone of an object (untyped)
  318. /// <summary>
  319. /// Create a clone of an object by serializing to JSON and the deserializing into a new instance
  320. /// </summary>
  321. /// <param name="source">The object to be cloned</param>
  322. /// <returns>A cloned instance</returns>
  323. public static object Clone(object source)
  324. {
  325. return Reparse(source.GetType(), source);
  326. }
  327. /// <summary>
  328. /// Clone an object into another instance
  329. /// </summary>
  330. /// <param name="dest">The object to clone to</param>
  331. /// <param name="source">The object to clone from</param>
  332. public static void CloneInto(object dest, object source)
  333. {
  334. ReparseInto(dest, source);
  335. }
  336. /// <summary>
  337. /// Reparse an object by writing to a stream and re-reading (possibly
  338. /// as a different type).
  339. /// </summary>
  340. /// <param name="type">The type of object to deserialize as</param>
  341. /// <param name="source">The source object to be reparsed</param>
  342. /// <returns>The newly parsed object instance</returns>
  343. public static object Reparse(Type type, object source)
  344. {
  345. if (source == null)
  346. return null;
  347. var ms = new MemoryStream();
  348. try
  349. {
  350. // Write
  351. var w = new StreamWriter(ms);
  352. Json.Write(w, source);
  353. w.Flush();
  354. // Read
  355. ms.Seek(0, SeekOrigin.Begin);
  356. var r = new StreamReader(ms);
  357. return Json.Parse(r, type);
  358. }
  359. finally
  360. {
  361. ms.Dispose();
  362. }
  363. }
  364. /// <summary>
  365. /// Reparse an object by writing to a stream and re-reading (possibly
  366. /// as a different type).
  367. /// </summary>
  368. /// <typeparam name="T">The type of object to deserialize as</typeparam>
  369. /// <param name="source">The source object to be reparsed</param>
  370. /// <returns>The newly parsed object instance</returns>
  371. public static T Reparse<T>(object source)
  372. {
  373. return (T)Reparse(typeof(T), source);
  374. }
  375. /// <summary>
  376. /// Reparse one object into another object
  377. /// </summary>
  378. /// <param name="dest">The destination object</param>
  379. /// <param name="source">The source object</param>
  380. public static void ReparseInto(object dest, object source)
  381. {
  382. var ms = new MemoryStream();
  383. try
  384. {
  385. // Write
  386. var w = new StreamWriter(ms);
  387. Json.Write(w, source);
  388. w.Flush();
  389. // Read
  390. ms.Seek(0, SeekOrigin.Begin);
  391. var r = new StreamReader(ms);
  392. Json.ParseInto(r, dest);
  393. }
  394. finally
  395. {
  396. ms.Dispose();
  397. }
  398. }
  399. /// <summary>
  400. /// Register a callback that can format a value of a particular type into json
  401. /// </summary>
  402. /// <param name="type">The type of object to be formatted</param>
  403. /// <param name="formatter">The formatter callback</param>
  404. public static void RegisterFormatter(Type type, Action<IJsonWriter, object> formatter)
  405. {
  406. JsonWriter._formatters.Set(type, formatter);
  407. }
  408. /// <summary>
  409. /// Register a callback to format the key values of dictionaries
  410. /// </summary>
  411. /// <remarks>
  412. /// These formatters are only used when writing .NET dictionary
  413. /// key instances - not when writing properties names.
  414. /// </remarks>
  415. /// <param name="type">The type of object to be formatted</param>
  416. /// <param name="formatter">The formatter callback</param>
  417. public static void RegisterKeyFormatter(Type type, Func<object, string> formatter)
  418. {
  419. JsonWriter._keyFormatters.Set(type, formatter);
  420. }
  421. /// <summary>
  422. /// Register a callback to format the key values of dictionaries
  423. /// </summary>
  424. /// <remarks>
  425. /// These formatters are only used when writing .NET dictionary
  426. /// key instances - not when writing properties names.
  427. /// </remarks>
  428. /// <typeparam name="T">The type of object to be formatted</typeparam>
  429. /// <param name="formatter">The formatter callback</param>
  430. public static void RegisterKeyFormatter<T>(Func<T, string> formatter)
  431. {
  432. JsonWriter._keyFormatters.Set(typeof(T), (o) => formatter((T)o));
  433. }
  434. /// <summary>
  435. /// Register a callback that can format a value of a particular type into json
  436. /// </summary>
  437. /// <typeparam name="T">The type of object to be formatted</typeparam>
  438. /// <param name="formatter">The formatter callback</param>
  439. public static void RegisterFormatter<T>(Action<IJsonWriter, T> formatter)
  440. {
  441. RegisterFormatter(typeof(T), (w, o) => formatter(w, (T)o));
  442. }
  443. /// <summary>
  444. /// Register a parser for a specified type
  445. /// </summary>
  446. /// <param name="type">The type of object to be parsed</param>
  447. /// <param name="parser">The parser callback</param>
  448. public static void RegisterParser(Type type, Func<IJsonReader, Type, object> parser)
  449. {
  450. JsonReader._parsers.Set(type, parser);
  451. }
  452. /// <summary>
  453. /// Register a parser for a specified type
  454. /// </summary>
  455. /// <typeparam name="T">The type of object to be parsed</typeparam>
  456. /// <param name="parser">The parser callback</param>
  457. public static void RegisterParser<T>(Func<IJsonReader, Type, T> parser)
  458. {
  459. RegisterParser(typeof(T), (r, t) => parser(r, t));
  460. }
  461. /// <summary>
  462. /// Registers a parser for a simple literal type
  463. /// </summary>
  464. /// <param name="type">The type to be parsed</param>
  465. /// <param name="parser">The parser callback</param>
  466. public static void RegisterParser(Type type, Func<object, object> parser)
  467. {
  468. RegisterParser(type, (r, t) => r.ReadLiteral(parser));
  469. }
  470. /// <summary>
  471. /// Register a parser for a simple literal type
  472. /// </summary>
  473. /// <typeparam name="T">The type to be parsed</typeparam>
  474. /// <param name="parser">The parser callback</param>
  475. public static void RegisterParser<T>(Func<object, T> parser)
  476. {
  477. RegisterParser(typeof(T), literal => parser(literal));
  478. }
  479. /// <summary>
  480. /// Register a parser for loading into an existing instance
  481. /// </summary>
  482. /// <param name="type">The type to be parsed</param>
  483. /// <param name="parser">The parser callback</param>
  484. public static void RegisterIntoParser(Type type, Action<IJsonReader, object> parser)
  485. {
  486. JsonReader._intoParsers.Set(type, parser);
  487. }
  488. /// <summary>
  489. /// Register a parser for loading into an existing instance
  490. /// </summary>
  491. /// <typeparam name="T">The type to be parsed</typeparam>
  492. /// <param name="parser">The parser callback</param>
  493. public static void RegisterIntoParser<T>(Action<IJsonReader, object> parser)
  494. {
  495. RegisterIntoParser(typeof(T), parser);
  496. }
  497. /// <summary>
  498. /// Register a factory for instantiating objects (typically abstract classes)
  499. /// Callback will be invoked for each key in the dictionary until it returns an object
  500. /// instance and which point it will switch to serialization using reflection
  501. /// </summary>
  502. /// <param name="type">The type to be instantiated</param>
  503. /// <param name="factory">The factory callback</param>
  504. public static void RegisterTypeFactory(Type type, Func<IJsonReader, string, object> factory)
  505. {
  506. JsonReader._typeFactories.Set(type, factory);
  507. }
  508. /// <summary>
  509. /// Register a callback to provide a formatter for a newly encountered type
  510. /// </summary>
  511. /// <param name="resolver">The resolver callback</param>
  512. public static void SetFormatterResolver(Func<Type, Action<IJsonWriter, object>> resolver)
  513. {
  514. JsonWriter._formatterResolver = resolver;
  515. }
  516. /// <summary>
  517. /// Register a callback to provide a parser for a newly encountered value type
  518. /// </summary>
  519. /// <param name="resolver">The resolver callback</param>
  520. public static void SetParserResolver(Func<Type, Func<IJsonReader, Type, object>> resolver)
  521. {
  522. JsonReader._parserResolver = resolver;
  523. }
  524. /// <summary>
  525. /// Register a callback to provide a parser for a newly encountered reference type
  526. /// </summary>
  527. /// <param name="resolver"></param>
  528. public static void SetIntoParserResolver(Func<Type, Action<IJsonReader, object>> resolver)
  529. {
  530. JsonReader._intoParserResolver = resolver;
  531. }
  532. static JsonOptions ResolveOptions(JsonOptions options)
  533. {
  534. JsonOptions resolved = JsonOptions.None;
  535. if ((options & (JsonOptions.WriteWhitespace|JsonOptions.DontWriteWhitespace))!=0)
  536. resolved |= options & (JsonOptions.WriteWhitespace | JsonOptions.DontWriteWhitespace);
  537. else
  538. resolved |= WriteWhitespaceDefault ? JsonOptions.WriteWhitespace : JsonOptions.DontWriteWhitespace;
  539. if ((options & (JsonOptions.StrictParser | JsonOptions.NonStrictParser)) != 0)
  540. resolved |= options & (JsonOptions.StrictParser | JsonOptions.NonStrictParser);
  541. else
  542. resolved |= StrictParserDefault ? JsonOptions.StrictParser : JsonOptions.NonStrictParser;
  543. return resolved;
  544. }
  545. }
  546. }