Как сериализовать DOM node в JSON, даже если есть круговые ссылки?

Я хочу сериализовать DOM node или даже целую window в JSON.

Например:

 >> serialize(document)
    -> {
      "URL": "http://stackoverflow.com/posts/2303713",
      "body": {
        "aLink": "",
        "attributes": [
          "getNamedItem": "function getNamedItem() { [native code] }",
          ...
        ],
        ...
        "ownerDocument": "#" // recursive link here
      },
      ...
    }

JSON.stringify()

JSON.stringify(window) // TypeError: Converting circular structure to JSON

Проблема заключается в том, что JSON по умолчанию не поддерживает циклические ссылки.

var obj = {}
obj.me = obj
JSON.stringify(obj) // TypeError: Converting circular structure to JSON

window и узлы DOM имеют многие из них. window === window.window, как и document.body.ownerDocument === document.

Кроме того, JSON.stringify не выполняет сериализацию функций, поэтому это не то, что я ищу.

dojox.json.ref

 `dojox.json.ref.toJson()` can easily serialize object with circular references:

    var obj = {}
    obj.me = obj
    dojox.json.ref.toJson(obj); // {"me":{"$ref":"#"}}

Хорошо, не так ли?

 dojox.json.ref.toJson(window) // Error: Can't serialize DOM nodes

Хорошо для меня недостаточно.

Почему?

Я пытаюсь сделать таблицу совместимости DOM для разных браузеров. Например, Webkit поддерживает атрибут placeholder, а Opera - нет, IE 8 поддерживает localStorage, а IE 7 - нет и т.д.

Я не хочу делать тысячи тестовых случаев. Я хочу сделать общий способ проверить их все.

Обновление, июнь 2013 г.

Я сделал прототип NV/dom-dom-dom.com.

Ответ 1

http://jsonml.org/ делает снимок грамматики для преобразования элементов DOM XHTML в JSON. Пример:

<ul>
    <li style="color:red">First Item</li>
    <li title="Some hover text." style="color:green">Second Item</li>
    <li><span class="code-example-third">Third</span> Item</li>
</ul>

становится

["ul",
    ["li", {"style": "color:red"}, "First Item"],
    ["li", {"title": "Some hover text.", "style": "color:green"}, "Second Item"],
    ["li", ["span", {"class": "code-example-third"}, "Third"], " Item" ]
]

Не использовал его еще, но думал об использовании его для проекта, где я хочу взять любую веб-страницу, и заново ее заново с помощью mustache.js.

Ответ 2

Вы можете потенциально пересечь DOM и создать чистое представление JS-объекта, а затем передать его в сериализатор DojoX. Но вы должны сначала решить, как вы планируете сопоставлять элементы DOM, их атрибуты и текстовые узлы, без двусмысленности, с объектами JS. Например, как бы вы представляли следующее?

<parent attr1="val1">
  Some text
  <child attr2="val2"><grandchild/></child>
</parent>

Как это?

{
    tag: "parent",
    attributes: [
        {
            name: "attr1",
            value: "val1"
        }
    ],
    children: [
        "Some text",
        {
            tag: "child",
            attributes: [
                {
                    name: "attr2",
                    value: "val2"
                }
            ],
            children: [
                { tag: "grandchild" }
            ]
         }
     ]
 }

Я думаю, что причина, по которой w580 не сразу поддерживает сериализацию DOM, может быть именно такой: необходимо сначала выбрать схему для сопоставления DOM с объектами JS. Существует ли стандартная схема, которая может быть использована? Будет ли ваш объект JS просто имитировать дерево DOM без каких-либо функций? Я думаю, вы должны сначала определить, каково ваше ожидание от "сериализации DOM к JSON".

Ответ 3

Я нашел это, и он отлично поработал у меня, когда я пытался преобразовать XML-строку в JSON.

XMLObjectifier.xmlToJSON(XMLObjectifier.textToXML(xmlString));

Возможно, это поможет.

Ответ 4

Похоже, вы должны написать это самостоятельно. Сериализованные данные JSON также не могут быть идеальным выбором для вашей задачи (таблица совместимости DOM). Возможно, вам придется перебирать объекты самостоятельно, проверять типы атрибутов и т.д.

var functions = [];
var strings = [];
for( var key in window ) {
    if( typeof window[key] == 'string' ) {
        strings[strings.length] = key;
    } else if( typeof window[key] == 'function' ) {
        functions[functions.length] = key;
    } else if( ... ) { ... }
}
...