views:

228

answers:

2

I am trying to create a sort of generic xml parser, like this:

Part 1:

An object with filter is created:

var product = {

  holder : {
    id: '',
    title: '',
    text : '',
    price: ''
  },

  filter : {
    id: '',
    test: function( elementHolder ) {
      if( elementHolder.id == product.filter.id ) {
        return true;
      }
      return false;
    }
  }
}

Part 2:

The xml parser:

/*
 * Generic XML parser
 * Parses the url into elementHolder objects
 */
var xmlParser = {

  parseXml: function( url, node, element, functie ) {

    var i = 0;       // result counter

    var result = []; // the result array
    /*
     * Open the xml file
     */
    jQuery.get( url, function( data ){

      /*
       * Loop through the results, if we have a filter, apply it.
       */
      jQuery( data ).find( node ).each( function(){
        /*
         * Copy the element holder
         */
        var elementHolder = element.holder;
        var elementData = jQuery(this);

        /*
         * Fill the copied holder with the xml data
         */
        elementData.children().each(function() {
          elementHolder[ this.nodeName ] = jQuery(this).text();
        });

        /*
         * if the filter applies, add the holder to the result
         */
        if( element.filter.test( elementHolder ) ) {
          result[i] = elementHolder; $i++;
        }           

        // console.log( result );

      });

      /*
       * If there is a callback ( prevents the result from parsing before it is ready )
       */
      try {
        if (typeof functie == "undefined") {
          throw 'There is no callback function specified.';
        }
      } catch( e ) { alert( e ); return; }

      functie( result );

    });    

  }

}

Part 3:

Calling on document ready:

product.filter.id = 11;

  /*
   * parsen
   */
  var productXml = 'test.xml';
  xmlParser.parseXml( productXml, "product", product, function( data ) {
    console.log( 'result:' );
    console.log( data );
  });

Part 4:

The test.xml:

<data>
  <products>
    <product>
      <id>1</id>
      <title>test 1</title>
      <text>text 1</text>
      <price>1.00</price>
    </product>
    <product>
      <id>2</id>
      <title>test 2</title>
      <text>text 2</text>
      <price>2.00</price>
    </product>
    <product>
      <id>3</id>
      <title>test 3</title>
      <text>text 3</text>
      <price>3.00</price>
    </product>
  </products>
</data>

It all works fine except for one thing;

The result get overwritten each loop by the newly created holder, does anyone have any ideas how to fix this? ( i think it is does have something to do with the scope ).

Thanks !

A: 

Stab in the dark - but I think you need a closure in your inner loop.

change this

elementData.children().each(function() {
  elementHolder[ this.nodeName ] = jQuery(this).text();
});

to this

elementData.children().each(function( e, n, t ) {
  return function()
  {
    e[n] = t;
  }
}( elementHolder, this.nodeName, jQuery(this).text() ));
Peter Bailey
Well, the part that says:elementData.children().each(function() { elementHolder[ this.nodeName ] = jQuery(this).text();});does work, the replacement code provides empty objects, should it really solve the problem that the object that does get passed into the result div is overwritten ?
FrankBr
+1  A: 

I found the solution

The part:

var elementHolder = element.holder;

Doesnt create a copy of the object, just another reference;

I found the solution to that on this page:

http://my.opera.com/GreyWyvern/blog/show.dml/1725165

Object.prototype.clone = function() {
  var newObj = (this instanceof Array) ? [] : {};
  for (i in this) {
    if (i == 'clone') continue;
    if (this[i] && typeof this[i] == "object") {
      newObj[i] = this[i].clone();
    } else newObj[i] = this[i]
  } return newObj;
};

var elementHolder = element.holder.clone();
FrankBr
This function bugts firefox, its even better to use this function:function clone( obj ){ if(obj == null || typeof(obj) != 'object') return obj; var temp = new obj.constructor(); for(var key in obj) temp[key] = clone(obj[key]); return temp;}
FrankBr