53

I want to serialize DOM node or even whole window to JSON.

For example:

 >> 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

The problem is JSON does not support circular references by default.

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

window and DOM nodes have many of them. window === window.window as will as document.body.ownerDocument === document.

Also, JSON.stringify does not serialize functions, so this is not what I'm looking for.

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":"#"}}

Good, isn't it?

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

Well not good enough for me.

Why?

I'm trying to make DOM compatibility table for different browsers. For instance, Webkit supports placeholder attribute and Opera doesn't, IE 8 supports localStorage and IE 7 doesn't, and so on.

I don't want to make thousands of test-cases. I want to make generic way for test them all.

Update, June 2013

I made a prototype NV/dom-dom-dom.com.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
NVI
  • 14,907
  • 16
  • 65
  • 104
  • I'm trying to make DOM compatibility table for different browsers. – NVI Feb 20 '10 at 21:23
  • 1
    How do you expect a DOM node to be serialized? What information do you want from a DOM node? – Gumbo Feb 20 '10 at 22:01
  • Gumbo, I just added example. Is it clear now? – NVI Feb 20 '10 at 22:03
  • FWIW, I still don't understand how serializing to JSON helps you with the "generic way" of testing compatibility. – Nickolay Feb 20 '10 at 23:51
  • I need to diff whole `window` object of different browsers. I don't know how to do it without serializing `window` to something like JSON. – NVI Feb 21 '10 at 00:02
  • Re: "Also, JSON.stringify does not serialize functions, so this is not what I'm looking for." Actually it does. Set JSONstring.includeFunctions = true; – David Sowsy Jul 15 '11 at 04:13
  • I have been working on a similar thing. Im making a remote debug console. The idea is that console.log can send a dom object or string to a remote PC using signalR. I have it working but when passing a Dom element it produced circular references. So I have started a domSerializer. Check out the site you should be able to debug and see the code. If you are interested we can get it on github. http://tomharris.net/Jasmine/run – Thomas Harris Nov 02 '13 at 09:24
  • @DavidSowsy "JSONstring.includeFunctions" what? Is that a typo? I am intrigued, but am turning up blank trying to Google for that. – Michael Jan 09 '14 at 18:52
  • @Michael I also was about to say "wow" but I wondered at the same time. This Q+A might clarify [http://stackoverflow.com/questions/2001449/is-it-valid-to-define-functions-in-json-results](http://stackoverflow.com/questions/2001449/is-it-valid-to-define-functions-in-json-results). – kabeleced Apr 11 '17 at 06:11

4 Answers4

16

http://jsonml.org/ takes a shot at a grammar for converting XHTML DOM elements into JSON. An an example:

<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>

becomes

["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" ]
]

Haven't used it yet, but thinking about using it for a project where I want to take any web page and re-template it using mustache.js.

Camilo Martin
  • 37,236
  • 20
  • 111
  • 154
Josh Dzielak
  • 1,803
  • 17
  • 19
5

You could potentially traverse the DOM and generate a pure JS object representation of it and then feed it to the DojoX serializer. But, you have to first decide how you're planning to map DOM elements, their attributes and the text nodes, without ambiguity, to JS objects. For example, how would you represent the following?

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

Like this?

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

I think the a reason DojoX doesn't immediately support DOM serialization could exactly be this: The need to first pick a scheme for mapping DOM to JS objects. Is there a standard scheme that could be employed? Would your JS object simply mimic a DOM tree without any functions? I think you have to first define what your expectation is from "serializing DOM to JSON".

Ates Goral
  • 137,716
  • 26
  • 137
  • 190
5

I found this and it worked great for me when I was trying to convert an XML string into JSON.

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

Maybe it will help.

Luc125
  • 5,752
  • 34
  • 35
Matthew Taylor
  • 3,911
  • 4
  • 29
  • 33
  • The script was moved to SourceForge acoording to a quick search for `xmlobjectifier jquery`. I found it a bit outdated, however. – Luc125 Dec 07 '15 at 01:15
3

It looks like you have to write it on your own. JSON serialized data may also not be the perfect choice for your task (DOM compatibility table). You probably have to iterate the objects on your own, check types of the attributes and so on..

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( ... ) { ... }
}
...
Frunsi
  • 7,099
  • 5
  • 36
  • 42
  • 1
    I've already made something like this in the `console._source_of()` http://github.com/NV/console.js/blob/gh-pages/console.js#L29-84 , but it does not handle circular references. Looks like I need to take the algorithm from dojox.json.ref and adopt it for my needs. – NVI Feb 20 '10 at 22:13