首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

JavaScript JSON 處理工具函數庫:安全解析、序列化與類型判斷

     編輯:Java 相關知識

JSON 作為前後端數據交換標準,安全高效的處理至關重要。本文整理一套輕量級 JSON 工具函數,涵蓋 XSS 防護解析、格式驗證、序列化/反序列化、精確類型判斷,代碼源自 Prototype、json2.js 等經典庫精華,適合直接集成到項目或作為學習參考。

1. 防 XSS 解析:unfilterJSON / evalJSON

後端返回 JSON 時,為防止直接 eval 導致 XSS,可約定將 JSON 包裹在註釋中:

/*-secure-
{"name": "Violet", "age": 25}
-*/

核心函數

const JSONFilter = /^\/\*-secure-\s*([\s\S]*?)\s*\*\/\s*$/;

function unfilterJSON(json, filter = JSONFilter) {
    return json.replace(filter, (_, captured) => captured || '');
}

function isJSONText(json) {
    // 源自 json2.js,驗證純 JSON 格式
    return /^[\],:{}\s]*$/
        .test(json.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
            .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
            .replace(/(?:^|:|,)(?:\s*\[)+/g, ''));
}

function evalJSON(json, sanitize = true) {
    if (typeof json !== 'string' || !json) return null;
    json = unfilterJSON(json);
    if (!sanitize || isJSONText(json)) {
        return window.JSON && window.JSON.parse
            ? window.JSON.parse(json)
            : new Function('return ' + json)();  // 兼容舊瀏覽器
    }
    throw new Error('Invalid JSON: ' + json);
}

使用示例

const response = '/*-secure-\n{"name":"Violet","age":25}\n*/';
const data = evalJSON(response);
console.log(data.name); // "Violet"

2. 序列化:toJSON

優先使用原生 JSON.stringify,降級實現支持 Date、RegExp、undefined、函數等特殊類型轉換。

function toJSON(obj) {
    if (window.JSON && window.JSON.stringify) return JSON.stringify(obj);

    const escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
    const meta = {'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'};

    function quote(str) {
        return '"' + str.replace(escapable, c => meta[c] || '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4)) + '"';
    }

    function f(n) { return n < 10 ? '0' + n : n; }

    if (obj === null) return 'null';
    const type = typeof obj;
    if (type === 'number') return isFinite(obj) ? String(obj) : 'null';
    if (type === 'string') return quote(obj);
    if (type === 'boolean') return String(obj);
    if (obj instanceof Date) {
        return '"' + obj.getUTCFullYear() + '-' + f(obj.getUTCMonth()+1) + '-' + f(obj.getUTCDate()) + 'T' +
               f(obj.getUTCHours()) + ':' + f(obj.getUTCMinutes()) + ':' + f(obj.getUTCSeconds()) + 'Z"';
    }
    if (Array.isArray(obj)) {
        const arr = obj.map(v => toJSON(v) === undefined ? 'null' : toJSON(v));
        return '[' + arr.join(',') + ']';
    }
    if (type === 'object') {
        const pairs = [];
        for (const k in obj) if (Object.prototype.hasOwnProperty.call(obj, k)) {
            const v = toJSON(obj[k]);
            if (v !== undefined) pairs.push(quote(k) + ':' + v);
        }
        return '{' + pairs.join(',') + '}';
    }
    return undefined; // 函數、undefined、Symbol 等忽略
}

3. 精確類型判斷:is / getType / isA

typeof 和 instanceof 存在跨框架、null/array 混淆等缺陷,推薦基於 Object.prototype.toString 的方案。

// 判斷 obj 是否為 type 類型(type 為 "Array", "Object", "Function", "Date", "RegExp", "String", "Number", "Boolean", "Null", "Undefined")
function is(obj, type) {
    if (type === 'Null') return obj === null;
    if (type === 'Undefined') return obj === void 0;
    if (type === 'Number') return typeof obj === 'number' && isFinite(obj);
    return Object.prototype.toString.call(obj).slice(8, -1) === type;
}

// 直接返回類型字符串
function getType(obj) {
    if (obj === null) return 'Null';
    if (obj === void 0) return 'Undefined';
    return Object.prototype.toString.call(obj).slice(8, -1);
}

// 原型鏈判斷(ES5+)
function isA(thing, canon) {
    if (thing == null || canon == null) return thing === canon;
    return Object.getPrototypeOf(Object(thing)) === Object.getPrototypeOf(Object(canon));
}

// 便捷判斷
const isBool = thing => isA(thing, true);
const isNumber = thing => isA(thing, 0) && isFinite(thing);
const isString = thing => isA(thing, '');

使用示例

console.log(getType([1,2,3]));        // "Array"
console.log(getType(null));           // "Null"
console.log(getType(undefined));      // "Undefined"
console.log(getType(document.getElementsByTagName('span'))); // "HTMLCollection"
console.log(getType(document.body.childNodes)); // "NodeList"

function forEach(array, fn, bind) {
    if (is(array, 'Array') && is(Array.forEach, 'Function')) {
        array.forEach(fn, bind);
    } else {
        for (let i = 0, n = array.length; i < n; i++) {
            i in array && fn.call(bind, array[i], i, array);
        }
    }
}

現代最佳實踐建議

  1. 優先原生 API:現代瀏覽器(IE10+)全支持 JSON.parse/stringify,性能最優、安全性最高。
  2. 避免 eval/new Function:僅在極舊環境(IE7-)降級使用,並嚴格驗證 isJSONText。
  3. CSP 防禦:設置 Content-Security-Policy: script-src 'self' 禁用內聯腳本,從根源阻斷 XSS。
  4. TypeScript 類型守衛:TS 項目中使用 isArray()、typeof、instanceof、用戶定義類型守衛,編譯期消除類型不確定性。
  5. 庫選擇:需求複雜時引入 fast-json-stringify(高性能序列化)、ajv(JSON Schema 驗證)、superjson(支持 Date/Set/Map/undefined 等擴展類型)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved