Discussion:
A different approach to parsing XML, and a little help on processing attributes more efficiently
augur
2010-01-28 18:32:35 UTC
Permalink
I spent a couple days looking all over the web for how to do this
well. I did some picking and choosing of methods, and I came up with
this. It works best in Safari, then FireFox, and then Chrome. I have
not tested in IE (do I have to?). It runs up to just over 20,000 lines
of XML well, over that memory is working too hard.

It is a very linear parser that descends from parent to child nodes as
it goes. I commented my code heavily so that it should be pretty
understandable. The one place that I could be a bit more efficient is
in the processing of the node attributes at each level. I would like
to parse out node attributes without specifying them (label and id are
pretty acceptable I guess), so any suggestions there would be great.

:::::The FrameWork::::::

jquery-1.4.min.js


:::::The code::::

$(function(){
$.ajax({
type: "GET",
url: "sites.xml",
dataType: "xml",
success: parseXml
});
function parseXml(xml) {
//get root element //
$(xml).find('sites').each(function(){
//get descentdent element = "<element></element" //
$(this).find('>element').each(function(){
//set variable as this item to pass to the next function //
var PARENT = $(this);
//get a bunch of attributes of the PARENT element. THIS COULD BE
MORE EFFICIENT //
parentLabel = $(this).attr('label');
parentID = $(this).attr('id');
siteLAT = $(this).attr('lat');
siteLNG = $(this).attr('lng');
//set variable as string of PARENT variables//
var parentBLOCK = "<br/>" +parentLabel +" " + parentID + "<br/>";
//set variable to post parentBLOCK//
var siteBLOCK = $("div").append(parentBLOCK);
//get descentdent element of PARENT //
$(PARENT).find('>element').each(function(){
//set variable as this item to pass to the next function //
var THISis = $(this);
//get attributes of the THISis element. THIS COULD BE MORE
EFFICIENT //
thisLabel = $(this).attr('label');
//set variable as string of THISis variables//
var thisBLOCK = thisLabel +": Has this latitude "+ siteLAT +" &
this Longitude "+siteLNG;
//set variable to post thisBLOCK//
siteBLOCK = $("div").append(thisBLOCK + "<br/>");
//get descentdent element = THISis//
$(THISis).find('>element').each(function(){
//get a bunch of attributes of the child elements. THIS COULD
BE MORE EFFICIENT //
childLabel = $(this).attr('label');
//get text from nodes of THISis chlidren elements //
childCopy = $(this).text();
//set variable as string of THISis child variables//
var childBLOCK = childLabel + ": " + childCopy + "<br/>";
//set variable to post childBLOCK//
siteBLOCK = $("div").append(childBLOCK);
});
});

});

});
}

});

:::::The XML::::

<?xml version="1.0" encoding="UTF-8"?>
<sites>
<element label="site" id="1" lat="66" lng="104">
<element label="Location">
<element label="city">SF</element>
<element label="state">CA</element>
<element label="region">West Coast</element>
</element>
</element>

<element label="site" id="2" lat="27" lng="305">
<element label="Location">
<element label="city">Las Vegas</element>
<element label="state">NV</element>
<element label="region">West</element>
</element>
</element>

<element label="site" id="3" lat="106" lng="35">
<element label="Location">
<element label="city">Pittsburgh</element>
<element label="state">Penn</element>
<element label="region">North East</element>
</element>
</element>

<element label="site" id="4" lat="77" lng="77">
<element label="Location">
<element label="city">Toledo</element>
<element label="state">Ohio</element>
<element label="region">North Mid</element>
</element>
</element>

</sites>
Michael Geary
2010-01-28 18:41:02 UTC
Permalink
Are you required to use XML? Why not use JSON and avoid all the parsing?

If you have to use XML I can make some suggestions to speed up the code a
bit. But let's see if that is a requirement or not first.

-Mike
Post by augur
I spent a couple days looking all over the web for how to do this
well. I did some picking and choosing of methods, and I came up with
this. It works best in Safari, then FireFox, and then Chrome. I have
not tested in IE (do I have to?). It runs up to just over 20,000 lines
of XML well, over that memory is working too hard.
It is a very linear parser that descends from parent to child nodes as
it goes. I commented my code heavily so that it should be pretty
understandable. The one place that I could be a bit more efficient is
in the processing of the node attributes at each level. I would like
to parse out node attributes without specifying them (label and id are
pretty acceptable I guess), so any suggestions there would be great.
jquery-1.4.min.js
$(function(){
$.ajax({
type: "GET",
url: "sites.xml",
dataType: "xml",
success: parseXml
});
function parseXml(xml) {
//get root element //
$(xml).find('sites').each(function(){
//get descentdent element = "<element></element" //
$(this).find('>element').each(function(){
//set variable as this item to pass to the next function //
var PARENT = $(this);
//get a bunch of attributes of the PARENT
element. THIS COULD BE
MORE EFFICIENT //
parentLabel = $(this).attr('label');
parentID = $(this).attr('id');
siteLAT = $(this).attr('lat');
siteLNG = $(this).attr('lng');
//set variable as string of PARENT variables//
var parentBLOCK = "<br/>" +parentLabel +" "
+ parentID + "<br/>";
//set variable to post parentBLOCK//
var siteBLOCK =
$("div").append(parentBLOCK);
//get descentdent element of PARENT //
$(PARENT).find('>element').each(function(){
//set variable as this item to pass
to the next function //
var THISis = $(this);
//get attributes of the THISis
element. THIS COULD BE MORE
EFFICIENT //
thisLabel = $(this).attr('label');
//set variable as string of THISis variables//
var thisBLOCK = thisLabel +": Has
this latitude "+ siteLAT +" &
this Longitude "+siteLNG;
//set variable to post thisBLOCK//
siteBLOCK =
$("div").append(thisBLOCK + "<br/>");
//get descentdent element = THISis//
$(THISis).find('>element').each(function(){
//get a bunch of
attributes of the child elements. THIS COULD
BE MORE EFFICIENT //
childLabel = $(this).attr('label');
//get text from
nodes of THISis chlidren elements //
childCopy = $(this).text();
//set variable as
string of THISis child variables//
var childBLOCK =
childLabel + ": " + childCopy + "<br/>";
//set variable to post childBLOCK//
siteBLOCK =
$("div").append(childBLOCK);
});
});
});
});
}
});
<?xml version="1.0" encoding="UTF-8"?>
<sites>
<element label="site" id="1" lat="66" lng="104">
<element label="Location">
<element label="city">SF</element>
<element label="state">CA</element>
<element label="region">West Coast</element>
</element>
</element>
<element label="site" id="2" lat="27" lng="305">
<element label="Location">
<element label="city">Las Vegas</element>
<element label="state">NV</element>
<element label="region">West</element>
</element>
</element>
<element label="site" id="3" lat="106" lng="35">
<element label="Location">
<element label="city">Pittsburgh</element>
<element label="state">Penn</element>
<element label="region">North East</element>
</element>
</element>
<element label="site" id="4" lat="77" lng="77">
<element label="Location">
<element label="city">Toledo</element>
<element label="state">Ohio</element>
<element label="region">North Mid</element>
</element>
</element>
</sites>
augur
2010-01-28 19:31:21 UTC
Permalink
Funny thing is, my lead engineer said the same thing...

Mostly this was an experiment to gain a better understanding of DOM
outside of HTML. This is about as exciting as a SAX parser when it
comes to speed (<5sec in safari for 20K lines, slightly longer in
FireFox, and noticeably longer in Chrome), though it is fairly
efficient, and gives me exactly the results I was wanting to see.
There are some practical applications.

Realizing that there is only so much memory out there more efficiency
is what I am going for, so any input on this script is helpful. I have
approached it this way because I get XML and DOM, and I am getting
decent at jQuery. Thus this method was in my range of current
understanding. JSON is something that I am just now becoming more
familiar with.
Michael Geary
2010-01-28 21:06:41 UTC
Permalink
No offense, but that code is really frightening. Not your fault though.

JSON is not only much faster than all this XML/DOM garbage, but it is *much*
easier to understand and use too!

If you're coding JavaScript, you need to know how to use ordinary JavaScript
objects and arrays, yes?

If you know how to work with those, then you *already know JSON*. Because
JSON is just JavaScript objects. (D'oh! JavaScript Object Notation!)

So here's your XML file transcribed fairly faithfully into JSON:

{
"sites": [
{
"label": "site",
"id": 2,
"lat": 27,
"lng": 305,
"location": {
"city": "Las Vegas",
"state": "NV",
"region": "West"
}
},
{
"label": "site",
"id": 3,
"lat": 106,
"lng": 35,
"location": {
"city": "Pittsburgh",
"state": "Penn",
"region": "North East"
}
}
]
}

I only included a couple of the entries to keep the example short. As you
can see, that's just a JavaScript object. This JavaScript object contains a
single property called 'sites'. That property is an array of objects, each
one representing a single site. Those site objects have the same properties
as your XML file.

So here's how you use it. I didn't try to exactly duplicate what your code
is doing - I wasn't sure what it was trying to do with all the $('div')
calls. Wouldn't those append to every DIV in your page? Oh - is there only a
single DIV that you're appending everything to? You should give it an ID and
reference it that way instead.

Also I realized I didn't quite duplicate what you're doing with those
label="Location" elements. It looks like you might have other kinds of
label="Something" elements as well? I didn't notice that, but tell you what,
here's the code as I understood it:

$.getJSON( 'sites.json', function( json ) {

var html = [];
html.push( '<div>' );

var sites = json.sites;
for( var site, i = -1; site = sites[++i]; ) {
var location = site.location;
html.push(
site.label, ' ', site.id, '<br/>',
'Lat: ', site.lat, ' Lng: ', site.lng, '<br/>',
location.city, ', ', location.state, '<br/>',
location.region
);
}

html.push( '</div>' );
$('#container').html( html.join('') );
});

As you can see, that is many times simpler - and *much* easier to understand
- than the XML parsing.

Some other speed notes...

* The for loop uses a slightly unusual form that is faster than the usual
numeric incrementing.

* Instead of appending elements to the HTML DOM as we go, we construct a
single HTML string and append it in one operation.

* That HTML string has an outermost DIV that wraps all the elements inside
it.

* We construct the HTML string not with += but by pushing the elements onto
an array and joining it at the end.

There is actually a way to speed this up a slight bit more in IE, but let's
leave that for the next installment. :-) As it is now, this code will be
many times faster than the XML code - at least one order of magnitude
faster, and maybe two.

Let me know what the real deal is with those label="location" elements and I
can suggest how to handle them.

-Mike
Post by augur
Funny thing is, my lead engineer said the same thing...
Mostly this was an experiment to gain a better understanding of DOM
outside of HTML. This is about as exciting as a SAX parser when it
comes to speed (<5sec in safari for 20K lines, slightly longer in
FireFox, and noticeably longer in Chrome), though it is fairly
efficient, and gives me exactly the results I was wanting to see.
There are some practical applications.
Realizing that there is only so much memory out there more efficiency
is what I am going for, so any input on this script is helpful. I have
approached it this way because I get XML and DOM, and I am getting
decent at jQuery. Thus this method was in my range of current
understanding. JSON is something that I am just now becoming more
familiar with.
augur
2010-01-29 02:32:59 UTC
Permalink
Mike,
Thanks so much for taking the time for writing this tutorial. I see
how this is effective. I attempted to do similar things with the XML
however...

The Data Objects for JSON are much more simplistic structurally, and a
little obtuse for reading visually. As I mentioned this simply is a
model that I am working on to traverse XML. The content is not
important. That I am able to traverse it is important. In a real world
application of this I would be working from an XML delivery. It does
not really make too much sense to convert the XML into JSON, though it
may make sense to have JSON be an output method for the mechanism
delivering the info (something to consider)....

I will look into using an array for building the output, since I am
passing the parent=>child variables I should be able to appendTo
("div") when I am done now. In a more complex layout I would worry
about ID, but for now since there is only one I do not need to worry
about it. The Location node was created to add a level of relationship
to be traversed. You could look at it as a product with variations,
there are Product attributes and Variant attributes all wrapped up in
one little bundle. Definitely not a mapping app that I am working on,
just the structure lent itself to my test, and also a number of
tutorials seems to use locational info.

I think that this XML parser could be more efficient in two ways.
First in traverse methodology, there has to be some way to declare
objects at the top and then process them. Additionally abstracting the
process for setting attributes as variables is a place this could get
better.
augur
2010-01-29 15:36:10 UTC
Permalink
Small Tweaks, less lines of code. Still no major breaks in breaking
the nested model for XML parsing. Things that I am interested in are
being able to determine levels of nesting abstractly, being able to
determine attributes abstractly, being able to hold results in an
Array, build the array, and then append the array.

function parseXml(xml) {
$(xml).find('sites').each(function(){
$(this).find('>element').each(function(){
var PARENT = $(this);
var siteBLOCK = $("div").append(PARENT.attr("label") +' '+
PARENT.attr("id") + "<br/>" );
$(PARENT).find('>element').each(function(){
var THISis = $(this);
siteBLOCK = $("div").append(THISis.attr('label') +": Has a
latitude of "+ PARENT.attr('lat') +" and a longitude of "+ PARENT.attr
('lng') + "<br/>" );
$(THISis).find('>element').each(function(){
var thisCHILD = $(this);
siteBLOCK = $("div").append(thisCHILD.attr('label') +": " +
thisCHILD.text() + "<br/>");
});
});
});
});
}
});
augur
2010-01-29 19:18:41 UTC
Permalink
This gets the variables at the top level and gives me the correct
count for each level.

function parseXml(xml) {

$(xml).find('sites').each(function(){

var PARENT = $(this).children($(this).attr('id'));
var PARENTcount = PARENT.length;
var PARENTSchildren = $(PARENT).children($(this).attr('label'));
var CHILDRENcount = PARENTSchildren.length;
var CHILDSchildren = $(PARENTSchildren).children($(this).attr
('label'));
var CHILDScount = CHILDSchildren.length;

alert(PARENTcount)
alert(CHILDRENcount)
alert(CHILDScount)


});
}
augur
2010-01-30 05:28:35 UTC
Permalink
Now I am treating each order of elements separately as their own
objects. This is much faster, if I can figure out how to append the
results correctly.

function parseXml(xml) {

$(xml).find('sites').each(function(){

PARENT = $(this);
var PARENTarr = jQuery.makeArray($(this).children($(this).attr
('id')));
var CHILDRENarr = jQuery.makeArray($(PARENTarr).children($(this).attr
('label')));
var CHILDSarr = jQuery.makeArray($(CHILDRENarr).children($(this).attr
('label')));

alert (PARENTarr.length + " "+ CHILDRENarr.length +" "+
CHILDSarr.length);

var PARENTobjs = {thisPARENT: $(PARENTarr).each(function(i){
var PARENTid = $(PARENTarr[i]).attr('id');
alert(PARENTid);
})
}

var CHILDRENobjs = {thisCHILDREN: $(CHILDRENarr).each(function(n){

var CHILDRENlabel = $(CHILDRENarr[n]).attr('label');
alert(CHILDRENlabel);
})
}

var CHILDSobjs = {thisCHILD:$(CHILDSarr).each(function(p){

var CHILDSlabel = $(CHILDSarr[p]).attr('label');
var CHILDStext = $(CHILDSarr[p]).text();
alert(CHILDSlabel + ": " + CHILDStext);
})
}
});
}
augur
2010-01-30 05:38:38 UTC
Permalink
this treats processes each order of element separately as a variable.
If I can append these correctly. I think that this might be a slightly
better process. It is at least treating things in a more object
oriented manner.

$(xml).find('sites').each(function(){

PARENT = $(this);
var PARENTarr = jQuery.makeArray($(this).children($(this).attr
('id')));
var CHILDRENarr = jQuery.makeArray($(PARENTarr).children($(this).attr
('label')));
var CHILDSarr = jQuery.makeArray($(CHILDRENarr).children($(this).attr
('label')));


var PARENTobjs = {thisPARENT: $(PARENTarr).each(function(i){
var PARENTid = $(PARENTarr[i]).attr('id');
alert(PARENTid);
})
}

var CHILDRENobjs = {thisCHILDREN: $(CHILDRENarr).each(function(n){

var CHILDRENlabel = $(CHILDRENarr[n]).attr('label');
alert(CHILDRENlabel);
})
}

var CHILDSobjs = {thisCHILD: $(CHILDSarr).each(function(p){

var CHILDSlabel = $(CHILDSarr[p]).attr('label');
var CHILDStext = $(CHILDSarr[p]).text();
alert(CHILDSlabel + ": " + CHILDStext);
})
}
});
}
augur
2010-02-02 22:31:59 UTC
Permalink
OK this is some progress. Still want more abstraction where I can,
though this is much improved. I also need to be able to get arrays of
attributes abstractly.

function parseXml(xml) {
$(xml).find('sites').each(function(){
var PARENTarr = jQuery.makeArray($(this).find('>element'));
var CHILDarr = jQuery.makeArray($(PARENTarr).find('>element'));
$.each(PARENTarr, function(i){
var PfromA = $(this);
var Pid = $(this).attr("label")+$(this).attr("id");
var Cid = $(CHILDarr[i]).attr("label");
$("div").append("<br/>"+Pid +"<br/>" + Cid +"<br/>");
$(CHILDarr[i]).each(function(){
var CHILDattrs = jQuery.makeArray($(this).find('>element'));
$(CHILDattrs).each(function(){
var CHILDid = $(this).attr('label') +": "+ $(this).text();
$("div").append(CHILDid +"<br/>");
});
});
});
});
}
});
augur
2010-02-02 22:47:57 UTC
Permalink
This is better. The arrays are much more efficient, and I cut the code
by more than half. I know, I know JSON, I will get there, this is
teaching me way more though. I still would like to be able to
determine my level of nesting abstractly, and also getting out non-
standard attributes.
function parseXml(xml) {
$(xml).find('sites').each(function(){
var PARENTarr = jQuery.makeArray($(this).find('>element'));
var CHILDarr = jQuery.makeArray($(PARENTarr).find('>element'));
$.each(PARENTarr, function(i){
var PfromA = $(this);
var Pid = $(this).attr("label")+$(this).attr("id");
var Cid = $(CHILDarr[i]).attr("label");
$("div").append("<br/>"+Pid +"<br/>" + Cid +"<br/>");
$(CHILDarr[i]).each(function(){
var CHILDattrs = jQuery.makeArray($(this).find('>element'));
$(CHILDattrs).each(function(){
var CHILDid = $(this).attr('label') +": "+ $(this).text();
$("div").append(CHILDid +"<br/>");
});
});
});
});
}
});
augur
2010-02-02 23:11:50 UTC
Permalink
This is better. The arrays are much more efficient, and I cut the code
by more than half. I know, I know JSON, I will get there, this is
teaching me way more though. I still would like to be able to
determine my level of nesting abstractly, and also getting out non-
standard attributes.

BTW processed over 50K lines in ~5seconds no errors.

function parseXml(xml) {
$(xml).find('sites').each(function(){
var PARENTarr = jQuery.makeArray($(this).find
('>element'));
var CHILDarr = jQuery.makeArray($(PARENTarr).find
('>element'));
$.each(PARENTarr, function(i){
var PfromA = $(this);
var Pid = $(this).attr
("label")+$(this).attr("id");
var Cid = $(CHILDarr
[i]).attr("label");
$("div").append("<br/
Post by augur
"+Pid +"<br/>" + Cid +"<br/>");
$(CHILDarr[i]).each
(function(){
var CHILDattrs
= jQuery.makeArray($(this).find('>element'));
$
(CHILDattrs).each(function(){
var
CHILDid = $(this).attr('label') +": "+ $(this).text();
$
("div").append(CHILDid +"<br/>");
});
});
});
});
}
augur
2010-02-03 00:10:11 UTC
Permalink
Let's me have multiple children:

function parseXml(xml) {
$(xml).find('sites').each(function(){
var PARENTarr = jQuery.makeArray($(this).find('>element'));
$(PARENTarr).each(function(i){
$("div").append("<br/>"+ $(this).attr("label")+(i+1) +"<br/>");
var CHILDarr = jQuery.makeArray($(PARENTarr[i]).find
('>element'));
$(CHILDarr).each(function(p){
$("div").append($(this).attr("label") +"<br/>");
var CHILDattrs = jQuery.makeArray($(CHILDarr[p]).find
('>element'));
$(CHILDattrs).each(function(){
var CHILDid = $(this).attr('label') +": "+ $(this).text();
$("div").append(CHILDid +"<br/>");
p=0;
});
});
});
});
}
});
Michael Geary
2010-02-03 00:57:39 UTC
Permalink
No worries on the XML vs. JSON. It's been interesting to watch your progress
in refactoring the code. I hope it's useful for other people too.

A few notes on the latest version...

* Avoid using ALLCAPS or PARTIALcaps in a variable name. Many JavaScript
programmers will think you intend such variables to be used as "constants".
(That is, a variable whose content is not intended to be changed.)

* You don't need all those makeArray calls.

* It's a bit confusing to mix the use of $ and jQuery in the same code. Use
one or the other consistently.

* Stop appending to $('div')!!!!!!!! Very bad habit! What happens when you
add another DIV to your page? Use a $('#container') selector instead.

* Even though you're still doing XML parsing, you will get much faster
performance by building an HTML string, and make sure the entire HTML
content is enclosed in one wrapper DIV.

* Watch out for all the duplicate $(something) selectors. If you use the
same $(something) more than once, than do this instead:

var $something = $(something);
$something.whatever(); // instead of $(something).whatever();

Putting those tips together, you get something like this:

function parseXml( xml ) {
var html = [];
html.push( '<div>' );
$(xml).find('sites').each( function() {
$(this).find('>element').each( function( i, parent ) {
var $parent = $(parent);
html.push( '<br/>', $parent.attr('label'), i + 1, '<br/>' );
$parent.find('>element').each( function( j, child ) {
var $child = $(child);
html.push( $child.attr('label'), '<br/>' );
$child.find('>element').each( function( k, inner ) {
var $inner = $(inner);
html.push( $inner.attr('label'), ': ', $inner.text(),
'<br/>' );
});
});
});
});
html.push( '</div>' );

$('#container').append( html.join('') );
}

-Mike
Post by augur
function parseXml(xml) {
$(xml).find('sites').each(function(){
var PARENTarr = jQuery.makeArray($(this).find('>element'));
$(PARENTarr).each(function(i){
$("div").append("<br/>"+
$(this).attr("label")+(i+1) +"<br/>");
var CHILDarr =
jQuery.makeArray($(PARENTarr[i]).find
('>element'));
$(CHILDarr).each(function(p){
$("div").append($(this).attr("label") +"<br/>");
var CHILDattrs =
jQuery.makeArray($(CHILDarr[p]).find
('>element'));
$(CHILDattrs).each(function(){
var CHILDid =
$(this).attr('label') +": "+ $(this).text();
$("div").append(CHILDid +"<br/>");
p=0;
});
});
});
});
}
});
augur
2010-02-03 03:18:36 UTC
Permalink
I was headed in this direction I was trying to get to the function
(i,parent),function(j,child).... I had not quite figured that part out
which is obvious. Let's see what this does.

Thanks
Chad
augur
2010-02-03 03:34:00 UTC
Permalink
That is really nice. 100K lines really fast no script time outs. I
need to get back and study a little more. I was so terribly close, but
missed some really basic principles.
augur
2010-02-03 06:27:45 UTC
Permalink
OK, so this is much better than a lesson in parsing XML. Showing the
errors that I had made in my process, you have also shown sound jQuery
which I was having a hard time picking up from the documentation. I
tend to learn best by doing and getting feedback. Mike, thank you. I
am going to keep this chain going with my mods as I make them.
Michael Geary
2010-02-03 06:54:37 UTC
Permalink
Cool, it will be good to see your continued work. We may be among the last
holdouts in this mailing list - but I figure since the thread started here
we can carry on.

BTW you may notice one bit of annoying repeated boilerplate in that last
version. See how the callback functions for all the .each() loops begin with
something like:

var $foobar = $(foobar);

Why don't we extend jQuery with a better version of .each() that does that
for us?

Let's call it .$each() as a reminder that it gives the callback a DOM
element that's already been wrapped with a $(). It would look like this:

jQuery.fn.$each = function( callback ) {
for( var element, i = -1; element = this[++i]; )
callback( jQuery(element), i );
};

Note that in addition to calling jQuery(element), it also reverses the
callback arguments - in most cases you only need the element inside the
callback - it's much less often that you need the index. (In your code, only
one of the four loops uses the index.)

So, using that function, the code can be written as:

function parseXml( xml ) {
var html = [];
html.push( '<div>' );
$(xml).find('sites').$each( function( $site ) {
$site.find('>element').$each( function( $parent, i ) {
html.push( '<br/>', $parent.attr('label'), i + 1, '<br/>' );
$parent.find('>element').$each( function( $child ) {
html.push( $child.attr('label'), '<br/>' );
$child.find('>element').$each( function( $inner ) {
html.push( $inner.attr('label'), ': ', $inner.text(),
'<br/>' );
});
});
});
});
html.push( '</div>' );

$('#container').append( html.join('') );
}

That's getting pretty clean!

Note that the code still uses the naming convention of $foobar for a jQuery
object - it helps make it clear which variables are jQuery objects.

-Mike
Post by augur
OK, so this is much better than a lesson in parsing XML. Showing the
errors that I had made in my process, you have also shown sound jQuery
which I was having a hard time picking up from the documentation. I
tend to learn best by doing and getting feedback. Mike, thank you. I
am going to keep this chain going with my mods as I make them.
Michael Geary
2010-02-03 06:56:55 UTC
Permalink
I don't know why Google Groups keeps inserting those semi-random blank lines
in the code! Now I'm curious, so I'm trying something a bit different -
let's see what happens..

function parseXml( xml ) {
var html = [];
html.push( '<div>' );
$(xml).find('sites').$each( function( $site ) {
$site.find('>element').$each( function( $parent, i ) {
html.push( '<br/>', $parent.attr('label'), i + 1, '<br/>' );
$parent.find('>element').$each( function( $child ) {
html.push( $child.attr('label'), '<br/>' );
$child.find('>element').$each( function( $inner ) {
html.push( $inner.attr('label'), ': ', $inner.text(),
'<br/>' );
});
});
});
});
html.push( '</div>' );

$('#container').append( html.join('') );
}
Post by Michael Geary
Cool, it will be good to see your continued work. We may be among the last
holdouts in this mailing list - but I figure since the thread started here
we can carry on.
BTW you may notice one bit of annoying repeated boilerplate in that last
version. See how the callback functions for all the .each() loops begin with
var $foobar = $(foobar);
Why don't we extend jQuery with a better version of .each() that does that
for us?
Let's call it .$each() as a reminder that it gives the callback a DOM
jQuery.fn.$each = function( callback ) {
for( var element, i = -1; element = this[++i]; )
callback( jQuery(element), i );
};
Note that in addition to calling jQuery(element), it also reverses the
callback arguments - in most cases you only need the element inside the
callback - it's much less often that you need the index. (In your code, only
one of the four loops uses the index.)
function parseXml( xml ) {
var html = [];
html.push( '<div>' );
$(xml).find('sites').$each( function( $site ) {
$site.find('>element').$each( function( $parent, i ) {
html.push( '<br/>', $parent.attr('label'), i + 1, '<br/>' );
$parent.find('>element').$each( function( $child ) {
html.push( $child.attr('label'), '<br/>' );
$child.find('>element').$each( function( $inner ) {
html.push( $inner.attr('label'), ': ', $inner.text(),
'<br/>' );
});
});
});
});
html.push( '</div>' );
$('#container').append( html.join('') );
}
That's getting pretty clean!
Note that the code still uses the naming convention of $foobar for a jQuery
object - it helps make it clear which variables are jQuery objects.
-Mike
Post by augur
OK, so this is much better than a lesson in parsing XML. Showing the
errors that I had made in my process, you have also shown sound jQuery
which I was having a hard time picking up from the documentation. I
tend to learn best by doing and getting feedback. Mike, thank you. I
am going to keep this chain going with my mods as I make them.
augur
2010-02-03 19:51:01 UTC
Permalink
Slick, and super fast. Makes it so that I do not even want to move
onto JSON ;-)... (JK, my engineers are forcing me too)

Made some minor mods, these add a little time and still processes over
50K lines in about 6-8 seconds, and 100K right about 10 seconds. Now
everything is its own html object ready for all the beauty of the rest
of jQuery. I wrapped each parent in an ID'ed span and each Location
and an ID'ed span.

OK this has gone in this direction to the point that I would have to
take a different approach to get it better.

My other goals are introspecting the XML to determine depth of the
PARENT => CHILD relationships. And to pull non-stadard (i.e. not id or
label) attributes and manipulate them.
augur
2010-02-05 08:13:24 UTC
Permalink
OK got non-standard attributes. It forces one rule, that all non-
standard attributes for an element must follow 'id' and 'label'. This
is standardized in setting "var z=2", as it skips past the first
attributes and finds the remaining. Though this is a lot of code for
each level of elements;

var attribs = $parent[0].attributes
attrb=[];
for (var z = 2; z < attribs.length; z++) {
attrb.push(attribs[z].name +":"+ attribs[z].value)
}
console.log(attrb)

Loading...