将 HTML 映射到 JSON

新手上路,请多包涵

我正在尝试将 HTML 映射到结构完整的 JSON。是否有任何库可以执行此操作,或者我需要自己编写吗?我想如果那里没有 html2json 库,我可以从 xml2json 库开始。毕竟,html 只是 xml 的变体,对吧?

更新: 好的,我应该举个例子。我想做的是以下内容。解析一串html:

 <div>
  <span>text</span>Text2
</div>

像这样变成一个 json 对象:

 {
  "type" : "div",
  "content" : [
    {
      "type" : "span",
      "content" : [
        "Text2"
      ]
    },
    "Text2"
  ]
}

注意:如果您没有注意到标签,我正在寻找 Javascript 中的解决方案

原文由 Julian Krispel-Samsel 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 394
2 个回答

我刚刚写了这个函数来做你想要的;尝试一下,如果它对您不起作用,请告诉我:

 // Test with an element.
var initElement = document.getElementsByTagName("html")[0];
var json = mapDOM(initElement, true);
console.log(json);

// Test with a string.
initElement = "<div><span>text</span>Text2</div>";
json = mapDOM(initElement, true);
console.log(json);

function mapDOM(element, json) {
    var treeObject = {};

    // If string convert to document Node
    if (typeof element === "string") {
        if (window.DOMParser) {
              parser = new DOMParser();
              docNode = parser.parseFromString(element,"text/xml");
        } else { // Microsoft strikes again
              docNode = new ActiveXObject("Microsoft.XMLDOM");
              docNode.async = false;
              docNode.loadXML(element);
        }
        element = docNode.firstChild;
    }

    //Recursively loop through DOM elements and assign properties to object
    function treeHTML(element, object) {
        object["type"] = element.nodeName;
        var nodeList = element.childNodes;
        if (nodeList != null) {
            if (nodeList.length) {
                object["content"] = [];
                for (var i = 0; i < nodeList.length; i++) {
                    if (nodeList[i].nodeType == 3) {
                        object["content"].push(nodeList[i].nodeValue);
                    } else {
                        object["content"].push({});
                        treeHTML(nodeList[i], object["content"][object["content"].length -1]);
                    }
                }
            }
        }
        if (element.attributes != null) {
            if (element.attributes.length) {
                object["attributes"] = {};
                for (var i = 0; i < element.attributes.length; i++) {
                    object["attributes"][element.attributes[i].nodeName] = element.attributes[i].nodeValue;
                }
            }
        }
    }
    treeHTML(element, treeObject);

    return (json) ? JSON.stringify(treeObject) : treeObject;
}

工作示例:http: //jsfiddle.net/JUSsf/ (在 Chrome 中测试,我不能保证完整的浏览器支持 - 你必须测试这个)。

它创建一个对象,其中包含您请求的格式的 HTML 页面的树结构,然后使用 JSON.stringify() 大多数现代浏览器(IE8+、Firefox 3+ .etc)中都包含该对象;如果您需要支持旧版浏览器,您可以包含 json2.js

它可以采用 DOM 元素或 string 包含有效的 XHTML 作为参数(我相信,我不确定 DOMParser() 在某些情况下是否会阻塞,因为它被设置为 "text/xml" 或者它是否不提供错误处理。不幸的是 "text/html" 浏览器支持很差)。

您可以通过将不同的值传递为 element 来轻松更改此函数的范围。无论您传递什么值,都将成为 JSON 映射的根。

原文由 George Reith 发布,翻译遵循 CC BY-SA 4.0 许可协议

html2json

表示复杂的 HTML 文档将很困难并且充满了极端情况,但我只是想分享一些技巧来展示如何开始这种程序。这个答案的不同之处在于它使用数据抽象和 toJSON 方法递归构建结果

下面, html2json 是一个 函数,它将一个 HTML 节点作为输入,并返回一个 JSON 字符串作为结果。特别注意代码是多么的扁平,但它仍然有足够的能力构建一个深度嵌套的树结构——所有这些都是可能的,而且复杂性几乎为零

 const Elem = e => ({
  tagName:
    e.tagName,
  textContent:
    e.textContent,
  attributes:
    Array.from(e.attributes, ({name, value}) => [name, value]),
  children:
    Array.from(e.children, Elem)
})

const html2json = e =>
  JSON.stringify(Elem(e), null, '  ')

console.log(html2json(document.querySelector('main')))
 <main>
  <h1 class="mainHeading">Some heading</h1>
  <ul id="menu">
    <li><a href="/a">a</a></li>
    <li><a href="/b">b</a></li>
    <li><a href="/c">c</a></li>
  </ul>
  <p>some text</p>
</main>

在前面的示例中, textContent 有点被屠杀了。为了解决这个问题,我们引入了另一个数据构造函数 TextElem 。我们必须映射 childNodes (而不是 children )并选择返回基于 e.nodeType 的正确数据类型 – this gets a little closer us–我们可能需要的

 const TextElem = e => ({
  type:
    'TextElem',
  textContent:
    e.textContent
})

const Elem = e => ({
  type:
    'Elem',
  tagName:
    e.tagName,
  attributes:
    Array.from(e.attributes, ({name, value}) => [name, value]),
  children:
    Array.from(e.childNodes, fromNode)
})

const fromNode = e => {
  switch (e?.nodeType) {
    case 1: return Elem(e)
    case 3: return TextElem(e)
    default: throw Error(`unsupported nodeType: ${e.nodeType}`)
  }
}

const html2json = e =>
  JSON.stringify(Elem(e), null, '  ')

console.log(html2json(document.querySelector('main')))
 <main>
  <h1 class="mainHeading">Some heading</h1>
  <ul id="menu">
    <li><a href="/a">a</a></li>
    <li><a href="/b">b</a></li>
    <li><a href="/c">c</a></li>
  </ul>
  <p>some text</p>
</main>

无论如何,这只是问题的两次迭代。当然,您必须解决出现的极端情况,但这种方法的优点在于,它为您提供了很大的灵活性,可以按照您希望的方式在 JSON 中对 HTML 进行编码, 而且 不会引入太多复杂性

根据我的经验,您可以不断迭代此技术并取得非常好的结果。如果这个答案对任何人都感兴趣并且希望我扩展任何内容,请告诉我 ^_^

相关: 使用 JavaScript 的递归方法:构建您自己的 JSON.stringify 版本


json2html

上面我们从 HTML 转到 JSON,现在我们可以从 JSON 转到 HTML。当我们可以在不丢失数据的情况下在两种数据类型之间进行转换时,这称为 同构。我们在这里所做的基本上就是写出上面每个函数的反函数 -

 const HtmlNode = (tagName, attributes = [], children = []) => {
  const e = document.createElement(tagName)
  for (const [k, v] of attributes) e.setAttribute(k, v)
  for (const child of children) e.appendChild(toNode(child))
  return e
}

const TextNode = (text) => {
  return document.createTextNode(text)
}

const toNode = t => {
  switch (t?.type) {
    case "Elem": return HtmlNode(t.tagName, t.attributes, t.children)
    case "TextElem": return TextNode(t.textContent)
    default: throw Error("unsupported type: " + t.type)
  }
}

const json2html = json =>
  toNode(JSON.parse(json))

const parsedJson =
  {"type":"Elem","tagName":"MAIN","attributes":[],"children":[{"type":"TextElem","textContent":"\n  "},{"type":"Elem","tagName":"H1","attributes":[["class","mainHeading"]],"children":[{"type":"TextElem","textContent":"Some heading"}]},{"type":"TextElem","textContent":"\n  "},{"type":"Elem","tagName":"UL","attributes":[["id","menu"]],"children":[{"type":"TextElem","textContent":"\n    "},{"type":"Elem","tagName":"LI","attributes":[],"children":[{"type":"Elem","tagName":"A","attributes":[["href","/a"]],"children":[{"type":"TextElem","textContent":"a"}]}]},{"type":"TextElem","textContent":"\n    "},{"type":"Elem","tagName":"LI","attributes":[],"children":[{"type":"Elem","tagName":"A","attributes":[["href","/b"]],"children":[{"type":"TextElem","textContent":"b"}]}]},{"type":"TextElem","textContent":"\n    "},{"type":"Elem","tagName":"LI","attributes":[],"children":[{"type":"Elem","tagName":"A","attributes":[["href","/c"]],"children":[{"type":"TextElem","textContent":"c"}]}]},{"type":"TextElem","textContent":"\n  "}]},{"type":"TextElem","textContent":"\n  "},{"type":"Elem","tagName":"P","attributes":[],"children":[{"type":"TextElem","textContent":"some text"}]},{"type":"TextElem","textContent":"\n"}]}

document.body.appendChild(toNode(parsedJson))

原文由 Mulan 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题