json-worker.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. // 创建一个处理BigInt的JSON解析器
  2. const JSONBigInt = {
  3. // 自定义的parse方法,处理大数字
  4. parse: function(text) {
  5. // 先尝试预处理字符串,将可能的大整数标记出来
  6. // 以更精确的方式匹配JSON中的大整数
  7. const preparedText = this._markBigInts(text);
  8. try {
  9. // 使用标准JSON解析,同时使用reviver函数还原BigInt
  10. return JSON.parse(preparedText, this._reviver);
  11. } catch (e) {
  12. // 如果处理失败,尝试原始解析方式
  13. console.error('BigInt处理失败,回退到标准解析', e);
  14. return JSON.parse(text);
  15. }
  16. },
  17. // 将JSON字符串中的大整数标记为特殊格式
  18. _markBigInts: function(text) {
  19. // 这个正则匹配JSON中的数字,但需要避免匹配到引号内的字符串
  20. // 匹配模式: 找到数字前面是冒号或左方括号的情况(表示这是个值而不是键名)
  21. return text.replace(
  22. /([:,\[]\s*)(-?\d{16,})([,\]\}])/g,
  23. function(match, prefix, number, suffix) {
  24. // 将大数字转换为特殊格式的字符串
  25. return prefix + '"__BigInt__' + number + '"' + suffix;
  26. }
  27. );
  28. },
  29. // 恢复函数,将标记的BigInt字符串转回BigInt类型
  30. _reviver: function(key, value) {
  31. // 检查是否是我们标记的BigInt字符串
  32. if (typeof value === 'string' && value.startsWith('__BigInt__')) {
  33. // 提取数字部分
  34. const numStr = value.substring(10);
  35. try {
  36. // 尝试转换为BigInt
  37. return BigInt(numStr);
  38. } catch (e) {
  39. // 如果转换失败,保留原始字符串
  40. console.warn('无法转换为BigInt:', numStr);
  41. return numStr;
  42. }
  43. }
  44. return value;
  45. }
  46. };
  47. // 处理主线程消息
  48. self.onmessage = function(event) {
  49. // 格式化JSON
  50. if (event.data.jsonString) {
  51. // 发送格式化中的消息
  52. self.postMessage(['FORMATTING']);
  53. try {
  54. // 先预处理JSON字符串,防止大整数丢失精度
  55. let jsonObj;
  56. try {
  57. // 尝试使用自定义的BigInt解析器
  58. jsonObj = JSONBigInt.parse(event.data.jsonString);
  59. } catch (e) {
  60. // 如果解析失败,回退到标准解析
  61. console.error('BigInt解析失败,回退到标准解析', e);
  62. jsonObj = JSON.parse(event.data.jsonString);
  63. }
  64. // 如果是简单主题,直接返回格式化的JSON
  65. if (event.data.skin && event.data.skin === 'theme-simple') {
  66. // 处理BigInt特殊情况
  67. let formatted = JSON.stringify(jsonObj, function(key, value) {
  68. if (typeof value === 'bigint') {
  69. // 移除n后缀,只显示数字本身
  70. return value.toString();
  71. }
  72. // 处理普通数字,避免科学计数法
  73. if (typeof value === 'number' && value.toString().includes('e')) {
  74. // 大数字转为字符串以避免科学计数法
  75. return value.toLocaleString('fullwide', {useGrouping: false});
  76. }
  77. return value;
  78. }, 4);
  79. let html = '<div id="formattedJson"><pre class="rootItem">' +
  80. formatted.replace(/&/g, '&amp;')
  81. .replace(/</g, '&lt;')
  82. .replace(/>/g, '&gt;')
  83. .replace(/"/g, '&quot;')
  84. .replace(/'/g, '&#039;') +
  85. '</pre></div>';
  86. self.postMessage(['FORMATTED', html]);
  87. return;
  88. }
  89. // 默认主题 - 创建更丰富的HTML结构
  90. let html = '<div id="formattedJson">' +
  91. formatJsonToHtml(jsonObj) +
  92. '</div>';
  93. self.postMessage(['FORMATTED', html]);
  94. } catch (e) {
  95. // 处理错误情况
  96. self.postMessage(['FORMATTED', '<div id="formattedJson"><div class="error">格式化失败: ' + e.message + '</div></div>']);
  97. }
  98. }
  99. };
  100. // HTML特殊字符格式化
  101. function htmlspecialchars(str) {
  102. str = str.replace(/&/g, '&amp;');
  103. str = str.replace(/</g, '&lt;');
  104. str = str.replace(/>/g, '&gt;');
  105. str = str.replace(/"/g, '&quot;');
  106. str = str.replace(/'/g, '&#039;');
  107. return str;
  108. }
  109. // 格式化字符串值,如果是URL则转换为链接
  110. function formatStringValue(str) {
  111. // URL正则表达式,匹配 http/https/ftp 协议的URL
  112. const urlRegex = /^(https?:\/\/|ftp:\/\/)[^\s<>"'\\]+$/i;
  113. if (urlRegex.test(str)) {
  114. // 如果是URL,转换为链接
  115. const escapedUrl = htmlspecialchars(str);
  116. return '<a href="' + escapedUrl + '" target="_blank" rel="noopener noreferrer">' + htmlspecialchars(str) + '</a>';
  117. } else {
  118. // 直接显示解析后的字符串内容,不需要重新转义
  119. // 这样可以保持用户原始输入的意图
  120. return htmlspecialchars(str);
  121. }
  122. }
  123. // 格式化JSON为HTML
  124. function formatJsonToHtml(json) {
  125. return createNode(json).getHTML();
  126. }
  127. // 创建节点
  128. function createNode(value) {
  129. let node = {
  130. type: getType(value),
  131. value: value,
  132. children: [],
  133. getHTML: function() {
  134. switch(this.type) {
  135. case 'string':
  136. return '<div class="item item-line"><span class="string">"' +
  137. formatStringValue(this.value) +
  138. '"</span></div>';
  139. case 'number':
  140. // 确保大数字不使用科学计数法
  141. let numStr = typeof this.value === 'number' && this.value.toString().includes('e')
  142. ? this.value.toLocaleString('fullwide', {useGrouping: false})
  143. : this.value;
  144. return '<div class="item item-line"><span class="number">' +
  145. numStr +
  146. '</span></div>';
  147. case 'bigint':
  148. // 对BigInt类型特殊处理,只显示数字,不添加n后缀
  149. return '<div class="item item-line"><span class="number">' +
  150. this.value.toString() +
  151. '</span></div>';
  152. case 'boolean':
  153. return '<div class="item item-line"><span class="bool">' +
  154. this.value +
  155. '</span></div>';
  156. case 'null':
  157. return '<div class="item item-line"><span class="null">null</span></div>';
  158. case 'object':
  159. return this.getObjectHTML();
  160. case 'array':
  161. return this.getArrayHTML();
  162. default:
  163. return '';
  164. }
  165. },
  166. getObjectHTML: function() {
  167. if (!this.value || Object.keys(this.value).length === 0) {
  168. return '<div class="item item-object"><span class="brace">{</span><span class="brace">}</span></div>';
  169. }
  170. let html = '<div class="item item-object">' +
  171. '<span class="expand"></span>' +
  172. '<span class="brace">{</span>' +
  173. '<span class="ellipsis"></span>' +
  174. '<div class="kv-list">';
  175. let keys = Object.keys(this.value);
  176. keys.forEach((key, index) => {
  177. let prop = this.value[key];
  178. let childNode = createNode(prop);
  179. html += '<div class="item">';
  180. // 如果值是对象或数组,在key前面添加展开按钮
  181. if (childNode.type === 'object' || childNode.type === 'array') {
  182. html += '<span class="expand"></span>';
  183. }
  184. html += '<span class="quote">"</span>' +
  185. '<span class="key">' + htmlspecialchars(key) + '</span>' +
  186. '<span class="quote">"</span>' +
  187. '<span class="colon">: </span>';
  188. // 添加值
  189. if (childNode.type === 'object' || childNode.type === 'array') {
  190. // 对于对象和数组,将开始大括号/方括号放在同一行,但不包含展开按钮
  191. html += childNode.getInlineHTMLWithoutExpand();
  192. } else {
  193. html += childNode.getHTML().replace(/^<div class="item item-line">/, '').replace(/<\/div>$/, '');
  194. }
  195. // 如果不是最后一个属性,添加逗号
  196. if (index < keys.length - 1) {
  197. html += '<span class="comma">,</span>';
  198. }
  199. html += '</div>';
  200. });
  201. html += '</div><span class="brace">}</span></div>';
  202. return html;
  203. },
  204. getArrayHTML: function() {
  205. if (!this.value || this.value.length === 0) {
  206. return '<div class="item item-array"><span class="brace">[</span><span class="brace">]</span></div>';
  207. }
  208. let html = '<div class="item item-array">' +
  209. '<span class="expand"></span>' +
  210. '<span class="brace">[</span>' +
  211. '<span class="ellipsis"></span>' +
  212. '<div class="kv-list">';
  213. this.value.forEach((item, index) => {
  214. let childNode = createNode(item);
  215. html += '<div class="item item-block">';
  216. // 如果数组元素是对象或数组,在前面添加展开按钮
  217. if (childNode.type === 'object' || childNode.type === 'array') {
  218. html += '<span class="expand"></span>';
  219. html += childNode.getInlineHTMLWithoutExpand();
  220. } else {
  221. html += childNode.getHTML().replace(/^<div class="item item-line">/, '').replace(/<\/div>$/, '');
  222. }
  223. // 如果不是最后一个元素,添加逗号
  224. if (index < this.value.length - 1) {
  225. html += '<span class="comma">,</span>';
  226. }
  227. html += '</div>';
  228. });
  229. html += '</div><span class="brace">]</span></div>';
  230. return html;
  231. },
  232. // 新增内联HTML方法,用于在同一行显示开始大括号/方括号
  233. getInlineHTML: function() {
  234. switch(this.type) {
  235. case 'object':
  236. return this.getInlineObjectHTML();
  237. case 'array':
  238. return this.getInlineArrayHTML();
  239. default:
  240. return this.getHTML();
  241. }
  242. },
  243. // 新增不包含展开按钮的内联HTML方法
  244. getInlineHTMLWithoutExpand: function() {
  245. switch(this.type) {
  246. case 'object':
  247. return this.getInlineObjectHTMLWithoutExpand();
  248. case 'array':
  249. return this.getInlineArrayHTMLWithoutExpand();
  250. default:
  251. return this.getHTML();
  252. }
  253. },
  254. getInlineObjectHTML: function() {
  255. if (!this.value || Object.keys(this.value).length === 0) {
  256. return '<span class="brace">{</span><span class="brace">}</span>';
  257. }
  258. let html = '<span class="brace">{</span>' +
  259. '<span class="expand"></span>' +
  260. '<span class="ellipsis"></span>' +
  261. '<div class="kv-list">';
  262. let keys = Object.keys(this.value);
  263. keys.forEach((key, index) => {
  264. let prop = this.value[key];
  265. let childNode = createNode(prop);
  266. html += '<div class="item">';
  267. // 如果值是对象或数组,在key前面添加展开按钮
  268. if (childNode.type === 'object' || childNode.type === 'array') {
  269. html += '<span class="expand"></span>';
  270. }
  271. html += '<span class="quote">"</span>' +
  272. '<span class="key">' + htmlspecialchars(key) + '</span>' +
  273. '<span class="quote">"</span>' +
  274. '<span class="colon">: </span>';
  275. // 添加值
  276. if (childNode.type === 'object' || childNode.type === 'array') {
  277. html += childNode.getInlineHTMLWithoutExpand();
  278. } else {
  279. html += childNode.getHTML().replace(/^<div class="item item-line">/, '').replace(/<\/div>$/, '');
  280. }
  281. // 如果不是最后一个属性,添加逗号
  282. if (index < keys.length - 1) {
  283. html += '<span class="comma">,</span>';
  284. }
  285. html += '</div>';
  286. });
  287. html += '</div><span class="brace">}</span>';
  288. return html;
  289. },
  290. getInlineArrayHTML: function() {
  291. if (!this.value || this.value.length === 0) {
  292. return '<span class="brace">[</span><span class="brace">]</span>';
  293. }
  294. let html = '<span class="brace">[</span>' +
  295. '<span class="expand"></span>' +
  296. '<span class="ellipsis"></span>' +
  297. '<div class="kv-list">';
  298. this.value.forEach((item, index) => {
  299. let childNode = createNode(item);
  300. html += '<div class="item item-block">';
  301. // 如果数组元素是对象或数组,在前面添加展开按钮
  302. if (childNode.type === 'object' || childNode.type === 'array') {
  303. html += '<span class="expand"></span>';
  304. html += childNode.getInlineHTMLWithoutExpand();
  305. } else {
  306. html += childNode.getHTML().replace(/^<div class="item item-line">/, '').replace(/<\/div>$/, '');
  307. }
  308. // 如果不是最后一个元素,添加逗号
  309. if (index < this.value.length - 1) {
  310. html += '<span class="comma">,</span>';
  311. }
  312. html += '</div>';
  313. });
  314. html += '</div><span class="brace">]</span>';
  315. return html;
  316. },
  317. getInlineObjectHTMLWithoutExpand: function() {
  318. if (!this.value || Object.keys(this.value).length === 0) {
  319. return '<span class="brace">{</span><span class="brace">}</span>';
  320. }
  321. let html = '<span class="brace">{</span>' +
  322. '<span class="ellipsis"></span>' +
  323. '<div class="kv-list">';
  324. let keys = Object.keys(this.value);
  325. keys.forEach((key, index) => {
  326. let prop = this.value[key];
  327. let childNode = createNode(prop);
  328. html += '<div class="item">';
  329. // 如果值是对象或数组,在key前面添加展开按钮
  330. if (childNode.type === 'object' || childNode.type === 'array') {
  331. html += '<span class="expand"></span>';
  332. }
  333. html += '<span class="quote">"</span>' +
  334. '<span class="key">' + htmlspecialchars(key) + '</span>' +
  335. '<span class="quote">"</span>' +
  336. '<span class="colon">: </span>';
  337. // 添加值
  338. if (childNode.type === 'object' || childNode.type === 'array') {
  339. html += childNode.getInlineHTMLWithoutExpand();
  340. } else {
  341. html += childNode.getHTML().replace(/^<div class="item item-line">/, '').replace(/<\/div>$/, '');
  342. }
  343. // 如果不是最后一个属性,添加逗号
  344. if (index < keys.length - 1) {
  345. html += '<span class="comma">,</span>';
  346. }
  347. html += '</div>';
  348. });
  349. html += '</div><span class="brace">}</span>';
  350. return html;
  351. },
  352. getInlineArrayHTMLWithoutExpand: function() {
  353. if (!this.value || this.value.length === 0) {
  354. return '<span class="brace">[</span><span class="brace">]</span>';
  355. }
  356. let html = '<span class="brace">[</span>' +
  357. '<span class="ellipsis"></span>' +
  358. '<div class="kv-list">';
  359. this.value.forEach((item, index) => {
  360. let childNode = createNode(item);
  361. html += '<div class="item item-block">';
  362. // 如果数组元素是对象或数组,在前面添加展开按钮
  363. if (childNode.type === 'object' || childNode.type === 'array') {
  364. html += '<span class="expand"></span>';
  365. html += childNode.getInlineHTMLWithoutExpand();
  366. } else {
  367. html += childNode.getHTML().replace(/^<div class="item item-line">/, '').replace(/<\/div>$/, '');
  368. }
  369. // 如果不是最后一个元素,添加逗号
  370. if (index < this.value.length - 1) {
  371. html += '<span class="comma">,</span>';
  372. }
  373. html += '</div>';
  374. });
  375. html += '</div><span class="brace">]</span>';
  376. return html;
  377. }
  378. };
  379. return node;
  380. }
  381. // 获取值类型
  382. function getType(value) {
  383. if (value === null) return 'null';
  384. if (value === undefined) return 'undefined';
  385. let type = typeof value;
  386. // 特别处理BigInt类型
  387. if (type === 'bigint') return 'bigint';
  388. if (type === 'object') {
  389. if (Array.isArray(value)) return 'array';
  390. }
  391. return type;
  392. }