JsonKit.cs 14 KB

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