123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779 |
- /**
- * FeHelper Json Format Tools
- */
- // 一些全局变量
- let editor = {};
- let LOCAL_KEY_OF_LAYOUT = 'local-layout-key';
- let JSON_LINT = 'jsonformat:json-lint-switch';
- let EDIT_ON_CLICK = 'jsonformat:edit-on-click';
- let AUTO_DECODE = 'jsonformat:auto-decode';
- new Vue({
- el: '#pageContainer',
- data: {
- defaultResultTpl: '<div class="x-placeholder"><img src="../json-format/json-demo.jpg" alt="json-placeholder"></div>',
- placeHolder: '',
- jsonFormattedSource: '',
- errorMsg: '',
- errorJsonCode: '',
- errorPos: '',
- jfCallbackName_start: '',
- jfCallbackName_end: '',
- jsonLintSwitch: true,
- autoDecode: false,
- fireChange: true,
- overrideJson: false,
- isInUSAFlag: false,
- autoUnpackJsonString: false,
- // JSONPath查询相关
- jsonPathQuery: '',
- showJsonPathModal: false,
- showJsonPathExamplesModal: false,
- jsonPathResults: [],
- jsonPathError: '',
- copyButtonState: 'normal', // normal, copying, success, error
- jsonPathExamples: [
- { path: '$', description: '根对象' },
- { path: '$.data', description: '获取data属性' },
- { path: '$.data.*', description: '获取data下的所有属性' },
- { path: '$.data[0]', description: '获取data数组的第一个元素' },
- { path: '$.data[*]', description: '获取data数组的所有元素' },
- { path: '$.data[?(@.name)]', description: '获取data数组中有name属性的元素' },
- { path: '$..name', description: '递归查找所有name属性' },
- { path: '$.data[0:3]', description: '获取data数组的前3个元素' },
- { path: '$.data[-1]', description: '获取data数组的最后一个元素' },
- { path: '$.*.price', description: '获取所有子对象的price属性' }
- ]
- },
- mounted: function () {
- // 自动开关灯控制
- DarkModeMgr.turnLightAuto();
- this.placeHolder = this.defaultResultTpl;
- this.autoDecode = localStorage.getItem(AUTO_DECODE);
- this.autoDecode = this.autoDecode === 'true';
- this.isInUSAFlag = this.isInUSA();
- this.jsonLintSwitch = (localStorage.getItem(JSON_LINT) !== 'false');
- this.overrideJson = (localStorage.getItem(EDIT_ON_CLICK) === 'true');
- this.changeLayout(localStorage.getItem(LOCAL_KEY_OF_LAYOUT));
- editor = CodeMirror.fromTextArea(this.$refs.jsonBox, {
- mode: "text/javascript",
- lineNumbers: true,
- matchBrackets: true,
- styleActiveLine: true,
- lineWrapping: true
- });
- //输入框聚焦
- editor.focus();
- // 格式化以后的JSON,点击以后可以重置原内容
- window._OnJsonItemClickByFH = (jsonTxt) => {
- if (this.overrideJson) {
- this.disableEditorChange(jsonTxt);
- }
- };
- editor.on('change', (editor, changes) => {
- this.jsonFormattedSource = editor.getValue().replace(/\n/gm, ' ');
- this.fireChange && this.format();
- });
- // 在tab创建或者更新时候,监听事件,看看是否有参数传递过来
- if (location.protocol === 'chrome-extension:') {
- chrome.tabs.query({currentWindow: true,active: true, }, (tabs) => {
- let activeTab = tabs.filter(tab => tab.active)[0];
- chrome.runtime.sendMessage({
- type: 'fh-dynamic-any-thing',
- thing: 'request-page-content',
- tabId: activeTab.id
- }).then(resp => {
- if(!resp || !resp.content) return ;
- editor.setValue(resp.content || '');
- this.format();
- });
- });
- }
- // 页面加载时自动获取并注入json-format页面的补丁
- this.loadPatchHotfix();
- },
- methods: {
- loadPatchHotfix() {
- // 页面加载时自动获取并注入页面的补丁
- chrome.runtime.sendMessage({
- type: 'fh-dynamic-any-thing',
- thing: 'fh-get-tool-patch',
- toolName: 'json-format'
- }, patch => {
- if (patch) {
- if (patch.css) {
- const style = document.createElement('style');
- style.textContent = patch.css;
- document.head.appendChild(style);
- }
- if (patch.js) {
- try {
- if (window.evalCore && window.evalCore.getEvalInstance) {
- window.evalCore.getEvalInstance(window)(patch.js);
- }
- } catch (e) {
- console.error('json-format补丁JS执行失败', e);
- }
- }
- }
- });
- },
- isInUSA: function () {
- // 通过时区判断是否在美国
- const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
- const isUSTimeZone = /^America\/(New_York|Chicago|Denver|Los_Angeles|Anchorage|Honolulu)/.test(timeZone);
- // 通过语言判断
- const language = navigator.language || navigator.userLanguage;
- const isUSLanguage = language.toLowerCase().indexOf('en-us') > -1;
- // 如果时区和语言都符合美国特征,则认为在美国
- return (isUSTimeZone && isUSLanguage);
- },
- format: function () {
- this.errorMsg = '';
- this.placeHolder = this.defaultResultTpl;
- this.jfCallbackName_start = '';
- this.jfCallbackName_end = '';
- let source = editor.getValue().replace(/\n/gm, ' ');
- if (!source) {
- return false;
- }
- // JSONP形式下的callback name
- let funcName = null;
- // json对象
- let jsonObj = null;
- // 下面校验给定字符串是否为一个合法的json
- try {
- // 再看看是不是jsonp的格式
- let reg = /^([\w\.]+)\(\s*([\s\S]*)\s*\)$/igm;
- let matches = reg.exec(source);
- if (matches != null) {
- funcName = matches[1];
- source = matches[2];
- }
- // 这里可能会throw exception
- jsonObj = JSON.parse(source);
- } catch (ex) {
- // new Function的方式,能自动给key补全双引号,但是不支持bigint,所以是下下策,放在try-catch里搞
- 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);
- } catch (ie) {
- // 最后给你一次机会,是个字符串,老夫给你再转一次
- jsonObj = new Function("return " + jsonObj)();
- }
- }
- } catch (exxx) {
- this.errorMsg = exxx.message;
- }
- }
- }
- 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);
- source = JSON.stringify(jsonObj);
- }
- // 是json格式,可以进行JSON自动格式化
- if (jsonObj != null && typeof jsonObj === "object" && !this.errorMsg.length) {
- try {
- let sortType = document.querySelectorAll('[name=jsonsort]:checked')[0].value;
- if (sortType !== '0') {
- jsonObj = JsonABC.sortObj(jsonObj, parseInt(sortType), true);
- }
- source = JSON.stringify(jsonObj);
- } catch (ex) {
- // 通过JSON反解不出来的,一定有问题
- this.errorMsg = ex.message;
- }
- if (!this.errorMsg.length) {
- if (this.autoDecode) {
- (async () => {
- let txt = await JsonEnDecode.urlDecodeByFetch(source);
- source = JsonEnDecode.uniDecode(txt);
- await Formatter.format(source);
- })();
- } else {
- (async () => {
- await Formatter.format(source);
- })();
- }
- this.placeHolder = '';
- this.jsonFormattedSource = source;
- // 如果是JSONP格式的,需要把方法名也显示出来
- if (funcName != null) {
- this.jfCallbackName_start = funcName + '(';
- this.jfCallbackName_end = ')';
- } else {
- this.jfCallbackName_start = '';
- this.jfCallbackName_end = '';
- }
- this.$nextTick(() => {
- this.updateWrapperHeight();
- })
- }
- }
- if (this.errorMsg.length) {
- if (this.jsonLintSwitch) {
- return this.lintOn();
- } else {
- this.placeHolder = '<span class="x-error">' + this.errorMsg + '</span>';
- return false;
- }
- }
- return true;
- },
- compress: function () {
- if (this.format()) {
- let jsonTxt = this.jfCallbackName_start + this.jsonFormattedSource + this.jfCallbackName_end;
- this.disableEditorChange(jsonTxt);
- }
- },
- autoDecodeFn: function () {
- this.$nextTick(() => {
- localStorage.setItem(AUTO_DECODE, this.autoDecode);
- this.format();
- });
- },
- uniEncode: function () {
- editor.setValue(JsonEnDecode.uniEncode(editor.getValue()));
- },
- uniDecode: function () {
- editor.setValue(JsonEnDecode.uniDecode(editor.getValue()));
- },
- urlDecode: function () {
- JsonEnDecode.urlDecodeByFetch(editor.getValue()).then(text => editor.setValue(text));
- },
- updateWrapperHeight: function () {
- let curLayout = localStorage.getItem(LOCAL_KEY_OF_LAYOUT);
- let elPc = document.querySelector('#pageContainer');
- if (curLayout === 'up-down') {
- elPc.style.height = 'auto';
- } else {
- elPc.style.height = Math.max(elPc.scrollHeight, document.body.scrollHeight) + 'px';
- }
- },
- changeLayout: function (type) {
- let elPc = document.querySelector('#pageContainer');
- if (type === 'up-down') {
- elPc.classList.remove('layout-left-right');
- elPc.classList.add('layout-up-down');
- this.$refs.btnLeftRight.classList.remove('selected');
- this.$refs.btnUpDown.classList.add('selected');
- } else {
- elPc.classList.remove('layout-up-down');
- elPc.classList.add('layout-left-right');
- this.$refs.btnLeftRight.classList.add('selected');
- this.$refs.btnUpDown.classList.remove('selected');
- }
- localStorage.setItem(LOCAL_KEY_OF_LAYOUT, type);
- this.updateWrapperHeight();
- },
- setCache: function () {
- this.$nextTick(() => {
- localStorage.setItem(EDIT_ON_CLICK, this.overrideJson);
- });
- },
- lintOn: function () {
- this.$nextTick(() => {
- localStorage.setItem(JSON_LINT, this.jsonLintSwitch);
- });
- if (!editor.getValue().trim()) {
- return true;
- }
- this.$nextTick(() => {
- if (!this.jsonLintSwitch) {
- return;
- }
- let lintResult = JsonLint.lintDetect(editor.getValue());
- if (!isNaN(lintResult.line)) {
- this.placeHolder = '<div id="errorTips">' +
- '<div id="tipsBox">错误位置:' + (lintResult.line + 1) + '行,' + (lintResult.col + 1) + '列;缺少字符或字符不正确</div>' +
- '<div id="errorCode">' + lintResult.dom + '</div></div>';
- }
- });
- return false;
- },
- disableEditorChange: function (jsonTxt) {
- this.fireChange = false;
- this.$nextTick(() => {
- editor.setValue(jsonTxt);
- this.$nextTick(() => {
- this.fireChange = true;
- })
- })
- },
- openOptionsPage: function(event){
- event.preventDefault();
- event.stopPropagation();
- chrome.runtime.openOptionsPage();
- },
- openDonateModal: function(event){
- event.preventDefault();
- event.stopPropagation();
- chrome.runtime.sendMessage({
- type: 'fh-dynamic-any-thing',
- thing: 'open-donate-modal',
- params: { toolName: 'json-format' }
- });
- },
- setDemo: function () {
- let demo = '{"BigIntSupported":995815895020119788889,"date":"20180322","url":"https://www.baidu.com?wd=fehelper","img":"http://gips0.baidu.com/it/u=1490237218,4115737545&fm=3028&app=3028&f=JPEG&fmt=auto?w=1280&h=720","message":"Success !","status":200,"city":"北京","count":632,"data":{"shidu":"34%","pm25":73,"pm10":91,"quality":"良","wendu":"5","ganmao":"极少数敏感人群应减少户外活动","yesterday":{"date":"21日星期三","sunrise":"06:19","high":"高温 11.0℃","low":"低温 1.0℃","sunset":"18:26","aqi":85,"fx":"南风","fl":"<3级","type":"多云","notice":"阴晴之间,谨防紫外线侵扰"},"forecast":[{"date":"22日星期四","sunrise":"06:17","high":"高温 17.0℃","low":"低温 1.0℃","sunset":"18:27","aqi":98,"fx":"西南风","fl":"<3级","type":"晴","notice":"愿你拥有比阳光明媚的心情"},{"date":"23日星期五","sunrise":"06:16","high":"高温 18.0℃","low":"低温 5.0℃","sunset":"18:28","aqi":118,"fx":"无持续风向","fl":"<3级","type":"多云","notice":"阴晴之间,谨防紫外线侵扰"},{"date":"24日星期六","sunrise":"06:14","high":"高温 21.0℃","low":"低温 7.0℃","sunset":"18:29","aqi":52,"fx":"西南风","fl":"<3级","type":"晴","notice":"愿你拥有比阳光明媚的心情"},{"date":"25日星期日","sunrise":"06:13","high":"高温 22.0℃","low":"低温 7.0℃","sunset":"18:30","aqi":71,"fx":"西南风","fl":"<3级","type":"晴","notice":"愿你拥有比阳光明媚的心情"},{"date":"26日星期一","sunrise":"06:11","high":"高温 21.0℃","low":"低温 8.0℃","sunset":"18:31","aqi":97,"fx":"西南风","fl":"<3级","type":"多云","notice":"阴晴之间,谨防紫外线侵扰"}]}}';
- editor.setValue(demo);
- this.$nextTick(() => {
- this.format();
- })
- },
- autoUnpackJsonStringFn: function () {
- this.$nextTick(() => {
- localStorage.setItem('jsonformat:auto-unpack-json-string', this.autoUnpackJsonString);
- this.format();
- });
- },
- // JSONPath查询功能
- executeJsonPath: function() {
- this.jsonPathError = '';
- this.jsonPathResults = [];
- if (!this.jsonPathQuery.trim()) {
- this.jsonPathError = '请输入JSONPath查询表达式';
- return;
- }
- let source = this.jsonFormattedSource || editor.getValue();
- if (!source.trim()) {
- this.jsonPathError = '请先输入JSON数据';
- return;
- }
- try {
- let jsonObj = JSON.parse(source);
- this.jsonPathResults = this.queryJsonPath(jsonObj, this.jsonPathQuery.trim());
- this.showJsonPathModal = true;
- } catch (error) {
- this.jsonPathError = 'JSON格式错误:' + error.message;
- this.showJsonPathModal = true;
- }
- },
- // JSONPath查询引擎
- queryJsonPath: function(obj, path) {
- let results = [];
-
- try {
- // 简化的JSONPath解析器
- if (path === '$') {
- results.push({ path: '$', value: obj });
- return results;
- }
- // 移除开头的$
- if (path.startsWith('$.')) {
- path = path.substring(2);
- } else if (path.startsWith('$')) {
- path = path.substring(1);
- }
- // 执行查询
- this.evaluateJsonPath(obj, path, '$', results);
-
- } catch (error) {
- throw new Error('JSONPath表达式错误:' + error.message);
- }
- return results;
- },
- // 递归评估JSONPath
- evaluateJsonPath: function(current, path, currentPath, results) {
- if (!path) {
- results.push({ path: currentPath, value: current });
- return;
- }
- // 处理递归搜索 ..
- if (path.startsWith('..')) {
- let remainPath = path.substring(2);
- this.recursiveSearch(current, remainPath, currentPath, results);
- return;
- }
- // 解析下一个路径片段
- let match;
-
- // 处理数组索引 [index] 或 [*] 或 [start:end]
- if ((match = path.match(/^\[([^\]]+)\](.*)$/))) {
- let indexExpr = match[1];
- let remainPath = match[2];
-
- if (!Array.isArray(current)) {
- return;
- }
- if (indexExpr === '*') {
- // 通配符:所有元素
- current.forEach((item, index) => {
- this.evaluateJsonPath(item, remainPath, currentPath + '[' + index + ']', results);
- });
- } else if (indexExpr.includes(':')) {
- // 数组切片 [start:end]
- let [start, end] = indexExpr.split(':').map(s => s.trim() === '' ? undefined : parseInt(s));
- let sliced = current.slice(start, end);
- sliced.forEach((item, index) => {
- let actualIndex = (start || 0) + index;
- this.evaluateJsonPath(item, remainPath, currentPath + '[' + actualIndex + ']', results);
- });
- } else if (indexExpr.startsWith('?(')) {
- // 过滤表达式 [?(@.prop)]
- current.forEach((item, index) => {
- if (this.evaluateFilter(item, indexExpr)) {
- this.evaluateJsonPath(item, remainPath, currentPath + '[' + index + ']', results);
- }
- });
- } else {
- // 具体索引
- let index = parseInt(indexExpr);
- if (index < 0) {
- index = current.length + index; // 负索引
- }
- if (index >= 0 && index < current.length) {
- this.evaluateJsonPath(current[index], remainPath, currentPath + '[' + index + ']', results);
- }
- }
- return;
- }
- // 处理属性访问 .property 或直接属性名
- if ((match = path.match(/^\.?([^.\[]+)(.*)$/))) {
- let prop = match[1];
- let remainPath = match[2];
-
- if (prop === '*') {
- // 通配符:所有属性
- if (typeof current === 'object' && current !== null) {
- Object.keys(current).forEach(key => {
- this.evaluateJsonPath(current[key], remainPath, currentPath + '.' + key, results);
- });
- }
- } else {
- // 具体属性
- if (typeof current === 'object' && current !== null && current.hasOwnProperty(prop)) {
- this.evaluateJsonPath(current[prop], remainPath, currentPath + '.' + prop, results);
- }
- }
- return;
- }
- // 处理方括号属性访问 ['property']
- if ((match = path.match(/^\['([^']+)'\](.*)$/))) {
- let prop = match[1];
- let remainPath = match[2];
-
- if (typeof current === 'object' && current !== null && current.hasOwnProperty(prop)) {
- this.evaluateJsonPath(current[prop], remainPath, currentPath + "['" + prop + "']", results);
- }
- return;
- }
- // 如果没有特殊符号,当作属性名处理
- if (typeof current === 'object' && current !== null && current.hasOwnProperty(path)) {
- results.push({ path: currentPath + '.' + path, value: current[path] });
- }
- },
- // 递归搜索
- recursiveSearch: function(current, targetProp, currentPath, results) {
- if (typeof current === 'object' && current !== null) {
- // 检查当前对象的属性
- if (current.hasOwnProperty(targetProp)) {
- results.push({ path: currentPath + '..' + targetProp, value: current[targetProp] });
- }
-
- // 递归搜索子对象
- Object.keys(current).forEach(key => {
- if (Array.isArray(current[key])) {
- current[key].forEach((item, index) => {
- this.recursiveSearch(item, targetProp, currentPath + '.' + key + '[' + index + ']', results);
- });
- } else if (typeof current[key] === 'object' && current[key] !== null) {
- this.recursiveSearch(current[key], targetProp, currentPath + '.' + key, results);
- }
- });
- }
- },
- // 简单的过滤器评估
- evaluateFilter: function(item, filterExpr) {
- // 简化的过滤器实现,只支持基本的属性存在性检查
- // 如 ?(@.name) 检查是否有name属性
- let match = filterExpr.match(/^\?\(@\.(\w+)\)$/);
- if (match) {
- let prop = match[1];
- return typeof item === 'object' && item !== null && item.hasOwnProperty(prop);
- }
-
- // 支持简单的比较 ?(@.age > 18)
- match = filterExpr.match(/^\?\(@\.(\w+)\s*([><=!]+)\s*(.+)\)$/);
- if (match) {
- let prop = match[1];
- let operator = match[2];
- let value = match[3];
-
- if (typeof item === 'object' && item !== null && item.hasOwnProperty(prop)) {
- let itemValue = item[prop];
- let compareValue = isNaN(value) ? value.replace(/['"]/g, '') : parseFloat(value);
-
- switch (operator) {
- case '>': return itemValue > compareValue;
- case '<': return itemValue < compareValue;
- case '>=': return itemValue >= compareValue;
- case '<=': return itemValue <= compareValue;
- case '==': return itemValue == compareValue;
- case '!=': return itemValue != compareValue;
- }
- }
- }
-
- return false;
- },
- // 显示JSONPath示例
- showJsonPathExamples: function() {
- this.showJsonPathExamplesModal = true;
- },
- // 使用JSONPath示例
- useJsonPathExample: function(path) {
- this.jsonPathQuery = path;
- this.closeJsonPathExamplesModal();
- },
- // 打开JSONPath查询模态框
- openJsonPathModal: function() {
- this.showJsonPathModal = true;
- // 清空之前的查询结果
- this.jsonPathResults = [];
- this.jsonPathError = '';
- this.copyButtonState = 'normal';
- },
- // 关闭JSONPath结果模态框
- closeJsonPathModal: function() {
- this.showJsonPathModal = false;
- this.copyButtonState = 'normal'; // 重置复制按钮状态
- },
- // 关闭JSONPath示例模态框
- closeJsonPathExamplesModal: function() {
- this.showJsonPathExamplesModal = false;
- },
- // 格式化JSONPath查询结果
- formatJsonPathResult: function(value) {
- if (typeof value === 'object') {
- return JSON.stringify(value, null, 2);
- }
- return String(value);
- },
- // 复制JSONPath查询结果
- copyJsonPathResults: function() {
- let resultText = this.jsonPathResults.map(result => {
- return `路径: ${result.path}\n值: ${this.formatJsonPathResult(result.value)}`;
- }).join('\n\n');
-
- // 设置复制状态
- this.copyButtonState = 'copying';
-
- navigator.clipboard.writeText(resultText).then(() => {
- this.copyButtonState = 'success';
- setTimeout(() => {
- this.copyButtonState = 'normal';
- }, 2000);
- }).catch(() => {
- // 兼容旧浏览器
- try {
- let textArea = document.createElement('textarea');
- textArea.value = resultText;
- document.body.appendChild(textArea);
- textArea.select();
- document.execCommand('copy');
- document.body.removeChild(textArea);
- this.copyButtonState = 'success';
- setTimeout(() => {
- this.copyButtonState = 'normal';
- }, 2000);
- } catch (error) {
- this.copyButtonState = 'error';
- setTimeout(() => {
- this.copyButtonState = 'normal';
- }, 2000);
- }
- });
- },
- // 下载JSONPath查询结果
- downloadJsonPathResults: function() {
- let resultText = this.jsonPathResults.map(result => {
- return `路径: ${result.path}\n值: ${this.formatJsonPathResult(result.value)}`;
- }).join('\n\n');
-
- // 基于JSONPath生成文件名
- let filename = this.generateFilenameFromPath(this.jsonPathQuery);
-
- let blob = new Blob([resultText], { type: 'text/plain;charset=utf-8' });
- let url = window.URL.createObjectURL(blob);
- let a = document.createElement('a');
- a.href = url;
- a.download = filename + '.txt';
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
- window.URL.revokeObjectURL(url);
- },
- // 根据JSONPath生成文件名
- generateFilenameFromPath: function(path) {
- if (!path || path === '$') {
- return 'jsonpath_root';
- }
-
- // 移除开头的$和.
- let cleanPath = path.replace(/^\$\.?/, '');
-
- // 替换特殊字符为下划线,保留数字、字母、点号、中划线
- let filename = cleanPath
- .replace(/[\[\]]/g, '_') // 方括号替换为下划线
- .replace(/[^\w\u4e00-\u9fa5.-]/g, '_') // 特殊字符替换为下划线,保留中文
- .replace(/_{2,}/g, '_') // 多个连续下划线合并为一个
- .replace(/^_|_$/g, ''); // 移除开头和结尾的下划线
-
- // 如果处理后为空,使用默认名称
- if (!filename) {
- return 'jsonpath_query';
- }
-
- // 限制文件名长度
- if (filename.length > 50) {
- filename = filename.substring(0, 50) + '_truncated';
- }
-
- return 'jsonpath_' + filename;
- },
- jumpToMockDataTool: function(event) {
- event.preventDefault();
- // 1. 先判断mock-data工具是否已安装
- // 方案:直接读取chrome.storage.local,判断DYNAMIC_TOOL:mock-data是否存在
- if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.local) {
- chrome.storage.local.get('DYNAMIC_TOOL:mock-data', result => {
- if (result && result['DYNAMIC_TOOL:mock-data']) {
- // 已安装,直接打开mock-data工具
- window.open('/mock-data/index.html', '_blank');
- } else {
- // 未安装,跳转到原href
- window.open('/options/index.html?query=数据Mock工具', '_blank');
- }
- });
- } else {
- // 兜底:如果无法访问chrome.storage,直接跳原href
- window.open('/options/index.html?query=数据Mock工具', '_blank');
- }
- }
- }
- });
- // 新增:递归解包嵌套JSON字符串的函数
- function deepParseJSONStrings(obj) {
- if (Array.isArray(obj)) {
- return obj.map(deepParseJSONStrings);
- } else if (typeof obj === 'object' && obj !== null) {
- const newObj = {};
- for (const key in obj) {
- if (!obj.hasOwnProperty(key)) continue;
- const val = obj[key];
- if (typeof val === 'string') {
- try {
- const parsed = JSON.parse(val);
- // 只递归对象或数组,且排除BigInt结构(如{s,e,c})和纯数字
- if (
- typeof parsed === 'object' &&
- parsed !== null &&
- (Array.isArray(parsed) || Object.prototype.toString.call(parsed) === '[object Object]') &&
- !(
- parsed &&
- typeof parsed.s === 'number' &&
- typeof parsed.e === 'number' &&
- Array.isArray(parsed.c) &&
- Object.keys(parsed).length === 3
- )
- ) {
- newObj[key] = deepParseJSONStrings(parsed);
- continue;
- }
- } catch (e) {}
- }
- newObj[key] = deepParseJSONStrings(val);
- }
- return newObj;
- }
- return obj;
- }
|