JsonKit.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  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. // Define JsonKit_NO_DYNAMIC to disable Expando support
  15. // Define JsonKit_NO_EMIT to disable Reflection.Emit
  16. // Define JsonKit_NO_DATACONTRACT to disable support for [DataContract]/[DataMember]
  17. using System;
  18. using System.IO;
  19. namespace Topten.JsonKit
  20. {
  21. // API
  22. public static class Json
  23. {
  24. static Json()
  25. {
  26. WriteWhitespaceDefault = true;
  27. StrictParserDefault = false;
  28. Json.SetFormatterResolver(Emit.MakeFormatter);
  29. Json.SetParserResolver(Emit.MakeParser);
  30. Json.SetIntoParserResolver(Emit.MakeIntoParser);
  31. }
  32. // Pretty format default
  33. public static bool WriteWhitespaceDefault
  34. {
  35. get;
  36. set;
  37. }
  38. // Strict parser
  39. public static bool StrictParserDefault
  40. {
  41. get;
  42. set;
  43. }
  44. // Write an object to a text writer
  45. public static void Write(TextWriter w, object o, JsonOptions options = JsonOptions.None)
  46. {
  47. var writer = new JsonWriter(w, ResolveOptions(options));
  48. writer.WriteValue(o);
  49. }
  50. public static bool SavePreviousVersions
  51. {
  52. get;
  53. set;
  54. }
  55. // Write a file atomically by writing to a temp file and then renaming it - prevents corrupted files if crash
  56. // in middle of writing file.
  57. public static void WriteFileAtomic(string filename, object o, JsonOptions options = JsonOptions.None, string backupFilename = null)
  58. {
  59. var tempName = filename + ".tmp";
  60. try
  61. {
  62. // Write the temp file
  63. WriteFile(tempName, o, (options | JsonOptions.Flush));
  64. if (System.IO.File.Exists(filename))
  65. {
  66. bool savePreviousVersion = false;
  67. if ((options & JsonOptions.AutoSavePreviousVersion)!=0)
  68. {
  69. savePreviousVersion = SavePreviousVersions;
  70. }
  71. else if ((options & JsonOptions.SavePreviousVersion)!=0)
  72. {
  73. savePreviousVersion = true;
  74. }
  75. // Work out backup filename
  76. if (savePreviousVersion)
  77. {
  78. // Make sure have a backup filename
  79. if (backupFilename == null)
  80. {
  81. backupFilename = filename + ".previous";
  82. }
  83. }
  84. else
  85. {
  86. // No backup
  87. backupFilename = null;
  88. }
  89. // Replace it
  90. int retry = 0;
  91. while (true)
  92. {
  93. try
  94. {
  95. File.Replace(tempName, filename, backupFilename);
  96. break;
  97. }
  98. catch (System.IO.IOException x)
  99. {
  100. retry++;
  101. if (retry >= 5)
  102. {
  103. 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);
  104. }
  105. System.Threading.Thread.Sleep(2000);
  106. }
  107. }
  108. }
  109. else
  110. {
  111. // Rename it
  112. File.Move(tempName, filename);
  113. }
  114. }
  115. catch
  116. {
  117. Utils.DeleteFile(tempName);
  118. throw;
  119. }
  120. }
  121. // Write an object to a file
  122. public static void WriteFile(string filename, object o, JsonOptions options = JsonOptions.None)
  123. {
  124. using (var w = new StreamWriter(filename))
  125. {
  126. Write(w, o, options);
  127. if ((options & JsonOptions.Flush) != 0)
  128. {
  129. w.Flush();
  130. w.BaseStream.Flush();
  131. }
  132. }
  133. }
  134. // Format an object as a json string
  135. public static string Format(object o, JsonOptions options = JsonOptions.None)
  136. {
  137. var sw = new StringWriter();
  138. var writer = new JsonWriter(sw, ResolveOptions(options));
  139. writer.WriteValue(o);
  140. return sw.ToString();
  141. }
  142. // Parse an object of specified type from a text reader
  143. public static object Parse(TextReader r, Type type, JsonOptions options = JsonOptions.None)
  144. {
  145. JsonReader reader = null;
  146. try
  147. {
  148. reader = new JsonReader(r, ResolveOptions(options));
  149. var retv = reader.Parse(type);
  150. reader.CheckEOF();
  151. return retv;
  152. }
  153. catch (Exception x)
  154. {
  155. var loc = reader == null ? new LineOffset() : reader.CurrentTokenPosition;
  156. Console.WriteLine("Exception thrown while parsing JSON at {0}, context:{1}\n{2}", loc, reader?.Context, x.ToString());
  157. throw new JsonParseException(x, reader?.Context, loc);
  158. }
  159. }
  160. // Parse an object of specified type from a text reader
  161. public static T Parse<T>(TextReader r, JsonOptions options = JsonOptions.None)
  162. {
  163. return (T)Parse(r, typeof(T), options);
  164. }
  165. // Parse from text reader into an already instantied object
  166. public static void ParseInto(TextReader r, Object into, JsonOptions options = JsonOptions.None)
  167. {
  168. if (into == null)
  169. throw new NullReferenceException();
  170. if (into.GetType().IsValueType)
  171. throw new InvalidOperationException("Can't ParseInto a value type");
  172. JsonReader reader = null;
  173. try
  174. {
  175. reader = new JsonReader(r, ResolveOptions(options));
  176. reader.ParseInto(into);
  177. reader.CheckEOF();
  178. }
  179. catch (Exception x)
  180. {
  181. var loc = reader == null ? new LineOffset() : reader.CurrentTokenPosition;
  182. Console.WriteLine("Exception thrown while parsing JSON at {0}, context:{1}\n{2}", loc, reader.Context, x.ToString());
  183. throw new JsonParseException(x,reader.Context,loc);
  184. }
  185. }
  186. // Parse an object of specified type from a file
  187. public static object ParseFile(string filename, Type type, JsonOptions options = JsonOptions.None)
  188. {
  189. using (var r = new StreamReader(filename))
  190. {
  191. return Parse(r, type, options);
  192. }
  193. }
  194. // Parse an object of specified type from a file
  195. public static T ParseFile<T>(string filename, JsonOptions options = JsonOptions.None)
  196. {
  197. using (var r = new StreamReader(filename))
  198. {
  199. return Parse<T>(r, options);
  200. }
  201. }
  202. // Parse from file into an already instantied object
  203. public static void ParseFileInto(string filename, Object into, JsonOptions options = JsonOptions.None)
  204. {
  205. using (var r = new StreamReader(filename))
  206. {
  207. ParseInto(r, into, options);
  208. }
  209. }
  210. // Parse an object from a string
  211. public static object Parse(string data, Type type, JsonOptions options = JsonOptions.None)
  212. {
  213. return Parse(new StringReader(data), type, options);
  214. }
  215. // Parse an object from a string
  216. public static T Parse<T>(string data, JsonOptions options = JsonOptions.None)
  217. {
  218. return (T)Parse<T>(new StringReader(data), options);
  219. }
  220. // Parse from string into an already instantiated object
  221. public static void ParseInto(string data, Object into, JsonOptions options = JsonOptions.None)
  222. {
  223. ParseInto(new StringReader(data), into, options);
  224. }
  225. // Create a clone of an object
  226. public static T Clone<T>(T source)
  227. {
  228. return (T)Reparse(source.GetType(), source);
  229. }
  230. // Create a clone of an object (untyped)
  231. public static object Clone(object source)
  232. {
  233. return Reparse(source.GetType(), source);
  234. }
  235. // Clone an object into another instance
  236. public static void CloneInto(object dest, object source)
  237. {
  238. ReparseInto(dest, source);
  239. }
  240. // Reparse an object by writing to a stream and re-reading (possibly
  241. // as a different type).
  242. public static object Reparse(Type type, object source)
  243. {
  244. if (source == null)
  245. return null;
  246. var ms = new MemoryStream();
  247. try
  248. {
  249. // Write
  250. var w = new StreamWriter(ms);
  251. Json.Write(w, source);
  252. w.Flush();
  253. // Read
  254. ms.Seek(0, SeekOrigin.Begin);
  255. var r = new StreamReader(ms);
  256. return Json.Parse(r, type);
  257. }
  258. finally
  259. {
  260. ms.Dispose();
  261. }
  262. }
  263. // Typed version of above
  264. public static T Reparse<T>(object source)
  265. {
  266. return (T)Reparse(typeof(T), source);
  267. }
  268. // Reparse one object into another object
  269. public static void ReparseInto(object dest, object source)
  270. {
  271. var ms = new MemoryStream();
  272. try
  273. {
  274. // Write
  275. var w = new StreamWriter(ms);
  276. Json.Write(w, source);
  277. w.Flush();
  278. // Read
  279. ms.Seek(0, SeekOrigin.Begin);
  280. var r = new StreamReader(ms);
  281. Json.ParseInto(r, dest);
  282. }
  283. finally
  284. {
  285. ms.Dispose();
  286. }
  287. }
  288. // Register a callback that can format a value of a particular type into json
  289. public static void RegisterFormatter(Type type, Action<IJsonWriter, object> formatter)
  290. {
  291. JsonWriter._formatters[type] = formatter;
  292. }
  293. // Typed version of above
  294. public static void RegisterFormatter<T>(Action<IJsonWriter, T> formatter)
  295. {
  296. RegisterFormatter(typeof(T), (w, o) => formatter(w, (T)o));
  297. }
  298. // Register a parser for a specified type
  299. public static void RegisterParser(Type type, Func<IJsonReader, Type, object> parser)
  300. {
  301. JsonReader._parsers.Set(type, parser);
  302. }
  303. // Register a typed parser
  304. public static void RegisterParser<T>(Func<IJsonReader, Type, T> parser)
  305. {
  306. RegisterParser(typeof(T), (r, t) => parser(r, t));
  307. }
  308. // Simpler version for simple types
  309. public static void RegisterParser(Type type, Func<object, object> parser)
  310. {
  311. RegisterParser(type, (r, t) => r.ReadLiteral(parser));
  312. }
  313. // Simpler and typesafe parser for simple types
  314. public static void RegisterParser<T>(Func<object, T> parser)
  315. {
  316. RegisterParser(typeof(T), literal => parser(literal));
  317. }
  318. // Register an into parser
  319. public static void RegisterIntoParser(Type type, Action<IJsonReader, object> parser)
  320. {
  321. JsonReader._intoParsers.Set(type, parser);
  322. }
  323. // Register an into parser
  324. public static void RegisterIntoParser<T>(Action<IJsonReader, object> parser)
  325. {
  326. RegisterIntoParser(typeof(T), parser);
  327. }
  328. // Register a factory for instantiating objects (typically abstract classes)
  329. // Callback will be invoked for each key in the dictionary until it returns an object
  330. // instance and which point it will switch to serialization using reflection
  331. public static void RegisterTypeFactory(Type type, Func<IJsonReader, string, object> factory)
  332. {
  333. JsonReader._typeFactories.Set(type, factory);
  334. }
  335. // Register a callback to provide a formatter for a newly encountered type
  336. public static void SetFormatterResolver(Func<Type, Action<IJsonWriter, object>> resolver)
  337. {
  338. JsonWriter._formatterResolver = resolver;
  339. }
  340. // Register a callback to provide a parser for a newly encountered value type
  341. public static void SetParserResolver(Func<Type, Func<IJsonReader, Type, object>> resolver)
  342. {
  343. JsonReader._parserResolver = resolver;
  344. }
  345. // Register a callback to provide a parser for a newly encountered reference type
  346. public static void SetIntoParserResolver(Func<Type, Action<IJsonReader, object>> resolver)
  347. {
  348. JsonReader._intoParserResolver = resolver;
  349. }
  350. // Resolve passed options
  351. static JsonOptions ResolveOptions(JsonOptions options)
  352. {
  353. JsonOptions resolved = JsonOptions.None;
  354. if ((options & (JsonOptions.WriteWhitespace|JsonOptions.DontWriteWhitespace))!=0)
  355. resolved |= options & (JsonOptions.WriteWhitespace | JsonOptions.DontWriteWhitespace);
  356. else
  357. resolved |= WriteWhitespaceDefault ? JsonOptions.WriteWhitespace : JsonOptions.DontWriteWhitespace;
  358. if ((options & (JsonOptions.StrictParser | JsonOptions.NonStrictParser)) != 0)
  359. resolved |= options & (JsonOptions.StrictParser | JsonOptions.NonStrictParser);
  360. else
  361. resolved |= StrictParserDefault ? JsonOptions.StrictParser : JsonOptions.NonStrictParser;
  362. return resolved;
  363. }
  364. }
  365. }