1
0
Эх сурвалжийг харах

修复jsonformat工具中,大整数精度丢失的问题; #494

zxlie 5 өдөр өмнө
parent
commit
543bb34905

+ 42 - 6
apps/json-format/format-lib.js

@@ -1115,9 +1115,18 @@ window.Formatter = (function () {
         _initElements();
 
         try {
-            // 先验证JSON是否有效
-            let parsedJson = JSON.parse(jsonStr);
-            cachedJsonString = JSON.stringify(parsedJson, null, 4);
+            // 先验证JSON是否有效(使用与worker一致的BigInt安全解析)
+            let parsedJson = _parseWithBigInt(jsonStr);
+            // 使用replacer保证bigint与大数字不丢精度
+            cachedJsonString = JSON.stringify(parsedJson, function(key, value) {
+                if (typeof value === 'bigint') {
+                    return value.toString();
+                }
+                if (typeof value === 'number' && value.toString().includes('e')) {
+                    return value.toLocaleString('fullwide', {useGrouping: false});
+                }
+                return value;
+            }, 4);
             jfPre.html(htmlspecialchars(cachedJsonString));
         } catch (e) {
             console.error('JSON解析失败:', e);
@@ -1171,9 +1180,17 @@ window.Formatter = (function () {
         formattingMsg.show();
         
         try {
-            // 先验证JSON是否有效
-            let parsedJson = JSON.parse(jsonStr);
-            cachedJsonString = JSON.stringify(parsedJson, null, 4);
+            // 先验证JSON是否有效(使用与worker一致的BigInt安全解析)
+            let parsedJson = _parseWithBigInt(jsonStr);
+            cachedJsonString = JSON.stringify(parsedJson, function(key, value) {
+                if (typeof value === 'bigint') {
+                    return value.toString();
+                }
+                if (typeof value === 'number' && value.toString().includes('e')) {
+                    return value.toLocaleString('fullwide', {useGrouping: false});
+                }
+                return value;
+            }, 4);
             
             // 设置原始JSON内容到jfPre(用于元数据按钮)
             jfPre.html(htmlspecialchars(cachedJsonString));
@@ -1206,6 +1223,25 @@ window.Formatter = (function () {
         }
     };
 
+    // 与 worker 保持一致的 BigInt 安全解析:
+    // 1) 给可能的大整数加标记;2) 使用reviver还原为原生BigInt
+    let _parseWithBigInt = function(text) {
+        // 允许数字后存在可选空白,再跟 , ] }
+        const marked = text.replace(/([:,\[]\s*)(-?\d{16,})(\s*)(?=(?:,|\]|\}))/g, function(match, prefix, number, spaces) {
+            return prefix + '"__BigInt__' + number + '"' + spaces;
+        });
+        return JSON.parse(marked, function(key, value) {
+            if (typeof value === 'string' && value.indexOf('__BigInt__') === 0) {
+                try {
+                    return BigInt(value.slice(10));
+                } catch (e) {
+                    return value.slice(10);
+                }
+            }
+            return value;
+        });
+    };
+
     // 工具函数:获取或创建唯一图片预览浮窗节点
     function getOrCreateImgPreview() {
         let $img = $('#fh-img-preview');

+ 43 - 19
apps/json-format/index.js

@@ -103,6 +103,26 @@ new Vue({
         this.loadPatchHotfix();
     },
     methods: {
+        // 安全的JSON.stringify:
+        // - 让 BigInt 在最终字符串中显示为未加引号的纯数字(用于显示与再解析)
+        // - 普通 number 若为科学计数法,转为完整字符串(仍是数字)
+        safeStringify(obj, space) {
+            const tagged = JSON.stringify(obj, function(key, value) {
+                if (typeof value === 'bigint') {
+                    // 用占位符标记,稍后去掉外层引号
+                    return `__FH_BIGINT__${value.toString()}`;
+                }
+                if (typeof value === 'number' && value.toString().includes('e')) {
+                    // 转成完整字符串,再在末尾转换为数字文本(通过占位)
+                    return `__FH_NUMSTR__${value.toLocaleString('fullwide', {useGrouping: false})}`;
+                }
+                return value;
+            }, space);
+            // 去掉占位符外层引号,恢复为裸数字文本
+            return tagged
+                .replace(/"__FH_BIGINT__(-?\d+)"/g, '$1')
+                .replace(/"__FH_NUMSTR__(-?\d+)"/g, '$1');
+        },
         // 安全获取localStorage值(在沙盒环境中可能不可用)
         safeGetLocalStorage(key) {
             try {
@@ -177,7 +197,7 @@ new Vue({
             // json对象
             let jsonObj = null;
 
-            // 下面校验给定字符串是否为一个合法的json
+            // 下面校验给定字符串是否为一个合法的json(优先:宽松修正 + BigInt 安全解析)
             try {
                 // 再看看是不是jsonp的格式
                 let reg = /^([\w\.]+)\(\s*([\s\S]*)\s*\)$/igm;
@@ -186,23 +206,18 @@ new Vue({
                     funcName = matches[1];
                     source = matches[2];
                 }
-                // 这里可能会throw exception
-                jsonObj = JSON.parse(source);
-
+                jsonObj = parseWithBigInt(source);
             } catch (ex) {
-                // new Function的方式,能自动给key补全双引号,但是不支持bigint,所以是下下策,放在try-catch里搞
+                // 兜底:仅当 BigInt 安全解析失败时,才尝试 eval 系列
                 try {
                     jsonObj = new Function("return " + source)();
                 } catch (exx) {
                     try {
-                        // 再给你一次机会,是不是下面这种情况:  "{\"ret\":\"0\", \"msg\":\"ok\"}"
                         jsonObj = new Function("return '" + source + "'")();
                         if (typeof jsonObj === 'string') {
                             try {
-                                // 确保bigint不会失真
-                                jsonObj = JSON.parse(jsonObj);
+                                jsonObj = parseWithBigInt(jsonObj);
                             } catch (ie) {
-                                // 最后给你一次机会,是个字符串,老夫给你再转一次
                                 jsonObj = new Function("return " + jsonObj)();
                             }
                         }
@@ -212,15 +227,6 @@ new Vue({
                 }
             }
 
-            try{
-                // 这里多做一个动作,给没有携带双引号的Key都自动加上,防止Long类型失真
-                const regex = /([{,]\s*)(\w+)(\s*:)/g;
-                source = source.replace(regex, '$1"$2"$3');
-                jsonObj = JSON.parse(source);
-            }catch(e){
-                // 这里什么动作都不需要做,这种情况下转换失败的,肯定是Value被污染了,抛弃即可
-            }
-
             // 新增:自动解包嵌套JSON字符串
             if (this.autoUnpackJsonString && jsonObj != null && typeof jsonObj === 'object') {
                 jsonObj = deepParseJSONStrings(jsonObj);
@@ -234,7 +240,7 @@ new Vue({
                     if (sortType !== '0') {
                         jsonObj = JsonABC.sortObj(jsonObj, parseInt(sortType), true);
                     }
-                    source = JSON.stringify(jsonObj);
+                    source = this.safeStringify(jsonObj);
                 } catch (ex) {
                     // 通过JSON反解不出来的,一定有问题
                     this.errorMsg = ex.message;
@@ -795,3 +801,21 @@ function deepParseJSONStrings(obj) {
     return obj;
 }
 
+
+// 统一的 BigInt 安全解析(与format-lib/worker思路一致):
+// 1) 自动给未加引号的 key 补双引号;2) 为可能的超长数字加标记;3) 用 reviver 还原为 BigInt
+function parseWithBigInt(text) {
+    // 补齐未加引号的 key
+    const keyFixRegex = /([\{,]\s*)(\w+)(\s*:)/g;
+    let fixed = String(text).replace(keyFixRegex, '$1"$2"$3');
+    // 标记 16 位及以上的整数(允许值后有空白,再跟 , ] } 或结尾)
+    fixed = fixed.replace(/([:,\[]\s*)(-?\d{16,})(\s*)(?=(?:,|\]|\}|$))/g, function(m, p1, num, sp) {
+        return p1 + '"__BigInt__' + num + '"' + sp;
+    });
+    return JSON.parse(fixed, function(key, value) {
+        if (typeof value === 'string' && value.indexOf('__BigInt__') === 0) {
+            try { return BigInt(value.slice(10)); } catch(e) { return value.slice(10); }
+        }
+        return value;
+    });
+}

+ 1 - 1
apps/manifest.json

@@ -1,7 +1,7 @@
 {
   "name": "FeHelper(前端助手)-Dev",
   "short_name": "FeHelper",
-  "version": "2025.6.2901",
+  "version": "2025.10.3001",
   "manifest_version": 3,
   "description": "JSON自动格式化、手动格式化,支持排序、解码、下载等,更多功能可在配置页按需安装!",
   "icons": {