parse.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. import BigNumber from "bignumber.js-master"
  2. // var BigNumber = null;
  3. // regexpxs extracted from
  4. // (c) BSD-3-Clause
  5. // https://github.com/fastify/secure-json-parse/graphs/contributors and https://github.com/hapijs/bourne/graphs/contributors
  6. const suspectProtoRx = /(?:_|\\u005[Ff])(?:_|\\u005[Ff])(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006[Ff])(?:t|\\u0074)(?:o|\\u006[Ff])(?:_|\\u005[Ff])(?:_|\\u005[Ff])/;
  7. const suspectConstructorRx = /(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)/;
  8. /*
  9. json_parse.js
  10. 2012-06-20
  11. Public Domain.
  12. NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
  13. This file creates a json_parse function.
  14. During create you can (optionally) specify some behavioural switches
  15. require('json-bigint')(options)
  16. The optional options parameter holds switches that drive certain
  17. aspects of the parsing process:
  18. * options.strict = true will warn about duplicate-key usage in the json.
  19. The default (strict = false) will silently ignore those and overwrite
  20. values for keys that are in duplicate use.
  21. The resulting function follows this signature:
  22. json_parse(text, reviver)
  23. This method parses a JSON text to produce an object or array.
  24. It can throw a SyntaxError exception.
  25. The optional reviver parameter is a function that can filter and
  26. transform the results. It receives each of the keys and values,
  27. and its return value is used instead of the original value.
  28. If it returns what it received, then the structure is not modified.
  29. If it returns undefined then the member is deleted.
  30. Example:
  31. // Parse the text. Values that look like ISO date strings will
  32. // be converted to Date objects.
  33. myData = json_parse(text, function (key, value) {
  34. var a;
  35. if (typeof value === 'string') {
  36. a =
  37. /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
  38. if (a) {
  39. return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
  40. +a[5], +a[6]));
  41. }
  42. }
  43. return value;
  44. });
  45. This is a reference implementation. You are free to copy, modify, or
  46. redistribute.
  47. This code should be minified before deployment.
  48. See http://javascript.crockford.com/jsmin.html
  49. USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
  50. NOT CONTROL.
  51. */
  52. /*members "", "\"", "\/", "\\", at, b, call, charAt, f, fromCharCode,
  53. hasOwnProperty, message, n, name, prototype, push, r, t, text
  54. */
  55. var json_parse = function (options) {
  56. 'use strict';
  57. // This is a function that can parse a JSON text, producing a JavaScript
  58. // data structure. It is a simple, recursive descent parser. It does not use
  59. // eval or regular expressions, so it can be used as a model for implementing
  60. // a JSON parser in other languages.
  61. // We are defining the function inside of another function to avoid creating
  62. // global variables.
  63. // Default options one can override by passing options to the parse()
  64. var _options = {
  65. strict: false, // not being strict means do not generate syntax errors for "duplicate key"
  66. storeAsString: false, // toggles whether the values should be stored as BigNumber (default) or a string
  67. alwaysParseAsBig: false, // toggles whether all numbers should be Big
  68. useNativeBigInt: false, // toggles whether to use native BigInt instead of bignumber.js
  69. protoAction: 'error',
  70. constructorAction: 'error',
  71. };
  72. // If there are options, then use them to override the default _options
  73. if (options !== undefined && options !== null) {
  74. if (options.strict === true) {
  75. _options.strict = true;
  76. }
  77. if (options.storeAsString === true) {
  78. _options.storeAsString = true;
  79. }
  80. _options.alwaysParseAsBig =
  81. options.alwaysParseAsBig === true ? options.alwaysParseAsBig : false;
  82. _options.useNativeBigInt =
  83. options.useNativeBigInt === true ? options.useNativeBigInt : false;
  84. if (typeof options.constructorAction !== 'undefined') {
  85. if (
  86. options.constructorAction === 'error' ||
  87. options.constructorAction === 'ignore' ||
  88. options.constructorAction === 'preserve'
  89. ) {
  90. _options.constructorAction = options.constructorAction;
  91. } else {
  92. throw new Error(
  93. `Incorrect value for constructorAction option, must be "error", "ignore" or undefined but passed ${options.constructorAction}`
  94. );
  95. }
  96. }
  97. if (typeof options.protoAction !== 'undefined') {
  98. if (
  99. options.protoAction === 'error' ||
  100. options.protoAction === 'ignore' ||
  101. options.protoAction === 'preserve'
  102. ) {
  103. _options.protoAction = options.protoAction;
  104. } else {
  105. throw new Error(
  106. `Incorrect value for protoAction option, must be "error", "ignore" or undefined but passed ${options.protoAction}`
  107. );
  108. }
  109. }
  110. }
  111. var at, // The index of the current character
  112. ch, // The current character
  113. escapee = {
  114. '"': '"',
  115. '\\': '\\',
  116. '/': '/',
  117. b: '\b',
  118. f: '\f',
  119. n: '\n',
  120. r: '\r',
  121. t: '\t',
  122. },
  123. text,
  124. error = function (m) {
  125. // Call error when something is wrong.
  126. throw {
  127. name: 'SyntaxError',
  128. message: m,
  129. at: at,
  130. text: text,
  131. };
  132. },
  133. next = function (c) {
  134. // If a c parameter is provided, verify that it matches the current character.
  135. if (c && c !== ch) {
  136. error("Expected '" + c + "' instead of '" + ch + "'");
  137. }
  138. // Get the next character. When there are no more characters,
  139. // return the empty string.
  140. ch = text.charAt(at);
  141. at += 1;
  142. return ch;
  143. },
  144. number = function () {
  145. // Parse a number value.
  146. var number,
  147. string = '';
  148. if (ch === '-') {
  149. string = '-';
  150. next('-');
  151. }
  152. while (ch >= '0' && ch <= '9') {
  153. string += ch;
  154. next();
  155. }
  156. if (ch === '.') {
  157. string += '.';
  158. while (next() && ch >= '0' && ch <= '9') {
  159. string += ch;
  160. }
  161. }
  162. if (ch === 'e' || ch === 'E') {
  163. string += ch;
  164. next();
  165. if (ch === '-' || ch === '+') {
  166. string += ch;
  167. next();
  168. }
  169. while (ch >= '0' && ch <= '9') {
  170. string += ch;
  171. next();
  172. }
  173. }
  174. number = +string;
  175. if (!isFinite(number)) {
  176. error('Bad number');
  177. } else {
  178. // if (BigNumber == null) BigNumber = require('bignumber.js');
  179. if (Number.isSafeInteger(number))
  180. return !_options.alwaysParseAsBig
  181. ? number
  182. : _options.useNativeBigInt
  183. ? BigInt(number)
  184. : new BigNumber(number);
  185. else
  186. // Number with fractional part should be treated as number(double) including big integers in scientific notation, i.e 1.79e+308
  187. return _options.storeAsString
  188. ? string
  189. : /[\.eE]/.test(string)
  190. ? number
  191. : _options.useNativeBigInt
  192. ? BigInt(string)
  193. : new BigNumber(string);
  194. }
  195. },
  196. string = function () {
  197. // Parse a string value.
  198. var hex,
  199. i,
  200. string = '',
  201. uffff;
  202. // When parsing for string values, we must look for " and \ characters.
  203. if (ch === '"') {
  204. var startAt = at;
  205. while (next()) {
  206. if (ch === '"') {
  207. if (at - 1 > startAt) string += text.substring(startAt, at - 1);
  208. next();
  209. return string;
  210. }
  211. if (ch === '\\') {
  212. if (at - 1 > startAt) string += text.substring(startAt, at - 1);
  213. next();
  214. if (ch === 'u') {
  215. uffff = 0;
  216. for (i = 0; i < 4; i += 1) {
  217. hex = parseInt(next(), 16);
  218. if (!isFinite(hex)) {
  219. break;
  220. }
  221. uffff = uffff * 16 + hex;
  222. }
  223. string += String.fromCharCode(uffff);
  224. } else if (typeof escapee[ch] === 'string') {
  225. string += escapee[ch];
  226. } else {
  227. break;
  228. }
  229. startAt = at;
  230. }
  231. }
  232. }
  233. error('Bad string');
  234. },
  235. white = function () {
  236. // Skip whitespace.
  237. while (ch && ch <= ' ') {
  238. next();
  239. }
  240. },
  241. word = function () {
  242. // true, false, or null.
  243. switch (ch) {
  244. case 't':
  245. next('t');
  246. next('r');
  247. next('u');
  248. next('e');
  249. return true;
  250. case 'f':
  251. next('f');
  252. next('a');
  253. next('l');
  254. next('s');
  255. next('e');
  256. return false;
  257. case 'n':
  258. next('n');
  259. next('u');
  260. next('l');
  261. next('l');
  262. return null;
  263. }
  264. error("Unexpected '" + ch + "'");
  265. },
  266. value, // Place holder for the value function.
  267. array = function () {
  268. // Parse an array value.
  269. var array = [];
  270. if (ch === '[') {
  271. next('[');
  272. white();
  273. if (ch === ']') {
  274. next(']');
  275. return array; // empty array
  276. }
  277. while (ch) {
  278. array.push(value());
  279. white();
  280. if (ch === ']') {
  281. next(']');
  282. return array;
  283. }
  284. next(',');
  285. white();
  286. }
  287. }
  288. error('Bad array');
  289. },
  290. object = function () {
  291. // Parse an object value.
  292. var key,
  293. object = Object.create(null);
  294. if (ch === '{') {
  295. next('{');
  296. white();
  297. if (ch === '}') {
  298. next('}');
  299. return object; // empty object
  300. }
  301. while (ch) {
  302. key = string();
  303. white();
  304. next(':');
  305. if (
  306. _options.strict === true &&
  307. Object.hasOwnProperty.call(object, key)
  308. ) {
  309. error('Duplicate key "' + key + '"');
  310. }
  311. if (suspectProtoRx.test(key) === true) {
  312. if (_options.protoAction === 'error') {
  313. error('Object contains forbidden prototype property');
  314. } else if (_options.protoAction === 'ignore') {
  315. value();
  316. } else {
  317. object[key] = value();
  318. }
  319. } else if (suspectConstructorRx.test(key) === true) {
  320. if (_options.constructorAction === 'error') {
  321. error('Object contains forbidden constructor property');
  322. } else if (_options.constructorAction === 'ignore') {
  323. value();
  324. } else {
  325. object[key] = value();
  326. }
  327. } else {
  328. object[key] = value();
  329. }
  330. white();
  331. if (ch === '}') {
  332. next('}');
  333. return object;
  334. }
  335. next(',');
  336. white();
  337. }
  338. }
  339. error('Bad object');
  340. };
  341. value = function () {
  342. // Parse a JSON value. It could be an object, an array, a string, a number,
  343. // or a word.
  344. white();
  345. switch (ch) {
  346. case '{':
  347. return object();
  348. case '[':
  349. return array();
  350. case '"':
  351. return string();
  352. case '-':
  353. return number();
  354. default:
  355. return ch >= '0' && ch <= '9' ? number() : word();
  356. }
  357. };
  358. // Return the json_parse function. It will have access to all of the above
  359. // functions and variables.
  360. return function (source, reviver) {
  361. var result;
  362. text = source + '';
  363. at = 0;
  364. ch = ' ';
  365. result = value();
  366. white();
  367. if (ch) {
  368. error('Syntax error');
  369. }
  370. // If there is a reviver function, we recursively walk the new structure,
  371. // passing each name/value pair to the reviver function for possible
  372. // transformation, starting with a temporary root object that holds the result
  373. // in an empty key. If there is not a reviver function, we simply return the
  374. // result.
  375. return typeof reviver === 'function'
  376. ? (function walk(holder, key) {
  377. var k,
  378. v,
  379. value = holder[key];
  380. if (value && typeof value === 'object') {
  381. Object.keys(value).forEach(function (k) {
  382. v = walk(value, k);
  383. if (v !== undefined) {
  384. value[k] = v;
  385. } else {
  386. delete value[k];
  387. }
  388. });
  389. }
  390. return reviver.call(holder, key, value);
  391. })({ '': result }, '')
  392. : result;
  393. };
  394. };
  395. module.exports = json_parse;