將一個html標記 轉 json

/** * 將一個html標記 轉 json串
 * @param {String} html 要處理的html標籤。html串必須有且僅有一個根節點。
 * @param {int} indent_str_count 起始縮進符個數
 * @param {String} indent_str 縮進符,默認tab鍵
 * @param {Object} indent 縮進對象
 * @return {String} json
 *                 {
 *                     "tag": "標籤名",
 *                     "attrs": {"屬性名": "屬性值", ...},
 *                     "content": ["標籤內容" | json  | [json數組]]    
 *                 }
 *         出錯返回結果: {"error": "html串必須包括一個根節點"}
 * @test 嵌套(nested):html2json("<table><tr><td class=\"popupmenu_option\" onclick=\"Do('detail')\">詳細結構</td></tr><tr><td class=\"popupmenu_option\" onclick=\"Do('rawdata')\">原始郵件</td><tr><td class=\"popupmenu_option\" onclick=\"Do('detail')\">詳細結構</td></tr></tr></table>")
 * @test 同級(sibling):html2json("<tr><td class=\"popupmenu_option\" onclick=\"Do('detail')\">詳細結構</td></tr><tr><td class=\"popupmenu_option\" onclick=\"Do('rawdata')\">原始郵件</td></tr>")
 * @create 堅 @ 2012-6-7 16:30:48
 * @modify [content字段的文本添加對"進行替換爲\"的轉義形式] by [堅] @ [2012-6-8 9:03:36]
 * @modify [content字段添加:對標籤內容若仍爲html標籤,則繼續轉json] by [堅] @ [2012-6-8 9:22:32]
 * @modify [添加json串縮進顯示] by [堅] @ [2012-6-8 11:54:27]
 * @modify [屬性組(attrs)JSON生成時,按空格分離屬性到數組(.split(/\s+/))的方式改成按正則匹配到數組(.match(/\w+=(?:"(?:[^"]|(?:\"))+?"|'(?:[^']|(?:\'))+?')/g);)的方式] by [堅] @ [2012年6月11日11:51:56]
 * @modify [添加對用戶傳入的html串作根/嵌套標籤和同級標籤的處理] by [堅] @ [2012-6-11 17:25:07]
 * @modify [log] by [user] @ [time]
 * @TODO: input img 這類單行標籤未處理
 */
function html2json(html, indent_str_count, indent_str, indent) {
    /// 常量設置
    var HTML_TAG_NAME = "tag";
    var HTML_TAG_ATTRIBUTES = "attrs";
    var HTML_TAG_CONTENT = "content";
    // HTML元素組
    var htmlEls = null;
    // 是否首次(非遞歸)執行。indent爲空說明是首次調用
    var isFirstRun = indent ? false : true;
     
    /// 驗證是否爲字符串,如果去除其先後導空格
    if (!html || typeof(html) != "string") {
        return null;
    }
    String.prototype.trim = String.prototype.trim || 
                function(){return this.replace(/^\s+|\s+$/g, "");};
    html = html.trim();
     
    /// 同級標籤解析,若match後長度等於1,說明不是同級標籤 */
    htmlEls = html.match(/<(\w+)([^>]*)>(.*?)<\/\1>/gi);
    if (!htmlEls) {
        // 首次調用(非遞歸),即html不符合要求,直接返null;不然生成content字段的內容並返回
        return isFirstRun ? null : "\"" + html.replace(/\"/g, "\\\"") + "\"";
    }
         
        /// 設置各類默認值
    // 默認縮進1個字符
    indent_str_count = indent_str_count || 1;
    // 默認縮進字符爲tab鍵
    indent_str = indent_str || "\t";
        // 縮進對象(共用)
    indent = indent || {
            "strings": [],
            "str": indent_str,
             /**
             * 獲取count個縮進字符
             * p.s. 寫完發現這個貌似有點像傳說中的享元模式,昨天剛看的,沒想到無心中寫出來了。
             *      因而果斷把函數名由get改成了factory。
             *      strings數組裏存放的就是共享的縮進單元,相同個數的縮進符 共享 同一個縮進單元。
             *      如,factory(5)都是 共享 strings[4]單元。
             * @param {int} count
             * @return {String} count個縮進字符
             * @test 
             * @create 堅 @ 2012-6-8 10:26:32
             * @modify [log] by [user] @ [time]
             */
            "factory": function(count) {
                         var index = count - 1;
                         if (!this.strings[index]) {
                             var string = new Array(count + 1).join(this.str);
                             this.strings[index] = string;
                         }
                         return this.strings[index];
                },
            "getIndentStr": function() {
                    return this.str || "\t";;
                }
        };
     
    if (htmlEls.length > 1) {
    /// html串內含多個同級標籤
        if (isFirstRun) {
            // 用戶傳進的html串爲同級標籤。
            return {"error": "html串必須包括一個根節點"};
        }
    var elsJson = "["
    for (var i = 0; ; i++) {
        elsJson += html2json(htmlEls[i], indent_str_count + 1, indent.getIndentStr(), indent);
        if (i >= htmlEls.length -1) {
            elsJson += "\n";
            break;
        }
        elsJson += ", ";
    }
    elsJson += indent.factory(indent_str_count - 1) + "]";
    return elsJson;
    } else if (htmlEls.length == 1) {
        /// html串爲一個根標籤 或是 一組嵌套標籤
        // 根標籤或嵌套標籤解析
        htmlEls = html.match(/^\s*<(\w+)([^>]*)>(.*)<\/\1>\s*$/gi);
        var tag = RegExp.$1;
        var content = RegExp.$3;
        var attrs = RegExp.$2.trim().match(/\w+=(?:"(?:[^"]|(?:\"))+?"|'(?:[^']|(?:\'))+?')/g);
    var json = "";
     
    json = "{\n" +
        indent.factory(indent_str_count) + "\"" + HTML_TAG_NAME + "\": \"" + tag + "\", \n" +
        indent.factory(indent_str_count) + "\"" + HTML_TAG_ATTRIBUTES + "\": " + 
            (function() {
                var attrsJson = "\"\"";
                if (attrs && attrs.length) {
                    attrsJson = "{\n";
                    for (var i = 0; ; i++) {
                        attrPair = attrs[i].split("=");
                        name = attrPair[0];
                        value = attrPair[1].replace(/^['"]?|['"]?$/g, "");
                        attrsJson += indent.factory(indent_str_count + 1) + 
                                        "\"" + name + "\": \"" + value + "\"";
                        if (i >= attrs.length - 1) {
                            attrsJson += "\n";
                            break;
                        }
                        attrsJson += ", \n";
                    }
                    attrsJson += indent.factory(indent_str_count)+ "}";
                }
                return attrsJson;
            })()  + ", \n" +
        indent.factory(indent_str_count) + "\"" + HTML_TAG_CONTENT + "\": " + 
            html2json(content, indent_str_count + 1, indent.getIndentStr(), indent) + "\n" + 
        indent.factory(indent_str_count-1) + "}";
        return json;
    }
}
相關文章
相關標籤/搜索