Post Archive
› June 28, 2005
Create DOM Objects From Strings in Safari
Working on a project recently, I encountered a scenario where I needed to parse a string of XML data. The project was Safari-specific.
The following is an account of my thought process and solution. With apologies to The Simpsons.
- I have some XML data, but it's in a string. That's bad.
- The
DOMParserclass has aparseFromStringmethod. That's good! - Safari lacks the
DOMParserclass. That's bad. - But Safari does have the
XMLHttpRequestclass. That's good! - The
XMLHttpRequestclass works on URLs, not strings. That's bad.
Then I remembered reading about data: URIs, and lo and behold Safari supports them. That's good! That's very good, because now I can pass a "local" data: URL to XMLHttpRequest and get back a nice DOM object. Here is what it looks like:
var xml = '<?xml version="1.0"?><foo>hello</foo>';
var url = "data:text/xml;charset=utf-8," + encodeURIComponent(xml);
// your standard AJAX stuff
var req = new XMLHttpRequest();
req.open("GET", url);
req.onload = function() { handleResponse(req) }
req.send(null);
function handleResponse(req) {
// w00t! a bona fide DOM object
var dom = req.responseXML;
alert(dom.documentElement.nodeName); // "foo"
}
There you have it. Until such a time that Safari supports the DOMParser class, this technique can be used to apply all the convenient DOM methods to XML that starts out life as a string.
Comments
1. June 28, 2005 07:10 PM
2. June 28, 2005 07:28 PM
David Lindquist Posted…
Yes, you can read a local file using XMLHttpRequest. I do this in the Google Maps widget to read the version number from the bundled Info.plist.
3. June 29, 2005 02:51 AM
Greg Posted…
Well, there is (in addition to NSXML) iconaradom which is a DOM framework written in Obj-C. The tool is listed at osx.hyperjeff.net/Apps. Also there a tool called www.saxonica.com which is however pure Java. Would be cool if there were a Cocoa-Obj-C clone of it!
4. June 29, 2005 01:24 PM
Erik Arvidsson Posted…
How about implementing the DOMParser object in js? As a matter of fact here you go:
if (typeof window.DOMParser != "undefined") {
function DOMParser() {}
DOMParser.prototype.parseFromString = function (str, contentType) {
if (window.ActiveXObject) {
var d = new ActiveXObject("MSXML.DomDocument");
d.loadXML(str);
return d;
} else if (window.XMLHttpRequest) {
var req = new XMLHttpRequest;
req.open("GET", "data:" + (contentType || "application/xml") +
";charset=utf-8," + encodeURIComponent(str), false);
if (req.overrideMimeType) {
req.overrideMimeType(contentType);
}
req.send(null);
return req.responseXML;
}
}
}
parseFromStream and baseURI are not implemented
5. June 29, 2005 01:32 PM
6. June 30, 2005 11:50 AM
cedric Posted…
Excellent work, Dave and Erik. I posted your piece of code for inclusion in the Sarissa project (I hope you have no objection).
7. July 4, 2005 11:30 AM
Manos Batsis Posted…
Using the data: URI scheme was a brilliant idea. I have already wrapped this in a DOMParser as suggested by Eric and probably will release it as sarissa 0.9.6.1. If only i had a mac ;-) Thanks, Manos8. July 4, 2005 11:30 AM
Manos Batsis Posted…
Using the data: URI scheme was a brilliant idea. I have already wrapped this in a DOMParser as suggested by Eric and probably will release it as sarissa 0.9.6.1. If only i had a mac ;-) Thanks, Manos9. July 17, 2005 05:42 PM
Erik Arvidsson Posted…
Did anyone get this to work in Safari and/or Konqueror?
I've heard that this isn't working for people so I tried it in Konqueror and there it works if accessed from the local file system but not over HTTP.
These are the platforms we've tried:
Ubuntu with Konq 3.4.0. Works with file but not with http.
Mac OS 10.3 with Safari 1.3.9 doesn't work (I don't know if it works using file). Same with Mac OS 10.4 with Safari 2.0.
10. July 18, 2005 07:56 PM
David Lindquist Posted…
D'oh! I never did test this over HTTP. The project I was working on (Dashboard widget) only uses local files, so I overlooked that possibility. Sorry for the false alarm everyone :(.
11. July 19, 2005 04:36 PM
Johnnie Blevins Posted…
There's another, easier way to do this. (I think)
The Range object has a method called createContextualFragment.
To use it, you just need to create a Range, place its end points, and call its createContextualFragment method with your string as an argument. The result is a document fragment that can be inserted into your document.
I've put together a little demo at DOMParsing
I chose to make it a method on String, but it would be just as easy to wrap it in a DOM parser, as Erik did above with the other code.
12. July 19, 2005 05:16 PM
Erik Arvidsson Posted…
Johnnie: Cool. I'll try that to see if that works in KHTML on XML documents but I doubt that it works. I hope I'm wrong
13. July 19, 2005 06:32 PM
Erik Arvidsson Posted…
Nope, Just like I suspected; createContextualFragment raises an exception when used on an XML document (both in Mozilla and KHTML).
Well, next version of Safari will have native support for DOMParser so until then... sorry Safari users :'(
14. February 15, 2006 03:56 PM
Mark Williams Posted…
Hi guys - now we've got DOMParser in Safari I would expect the prototyped loadXML I have for Mozilla to work in Safari, but its erroring like I'm not prototyping the right object.
Document.prototype.loadXML = function(strXML) {
var objDOMParser = new DOMParser();
var objDoc = objDOMParser.parseFromString...etc. etc - Any ideas??
Paul D Posted…
Can something like this work to read XML data from a local file? I'm interested in designing a widget that does this, but I've done very little Javascript and haven't figured out how to read the data in.