Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to parse XML in Meteor backend?

I'm trying to parse XML with an HTTP call in the back end where the body is in XML. The problem is we can't use Jquery or DOMParser in the back end to parse. We tried to first parse it in the front end, and then send it to the meteor method (in the back end) as a variable, but it was too big (exceed maximum stack). We also tried to add a node module to do it but it didn't work properly.

How can we get a parsed XML object in the back end of Meteor?

like image 393
yimmy Avatar asked Mar 21 '15 15:03

yimmy


1 Answers

First you will need to install a couple of packages.

  1. HTTP, so you can "GET" the external XML
  2. xml2js, so you can parse the XML once it shows up

Install and restart the server like so:

meteor add http peerlibrary:xml2js && meteor

Second, build a server-side function. It doesn't have to be a Meteor method, but for this example, I'll put it in one so we can easily fire it. Use HTTP.call() or HTTP.get() to make the request. Then the URL; I'm going to use an example XML file. Then the after-URL parameters; in my case there are none. Then the callback function.

Meteor.methods({
    'xmlDemo':function(){
        HTTP.call('GET',
            'http://www.xmlfiles.com/examples/plant_catalog.xml',
            {},
            function(callError,callResponse){
                console.log('xml',callResponse);
            }
        );
    }
});

If you're new to Meteor, take special note of the "error" parameter first in the callback function.

Third, try out the method. This is like a game of telephone.

  1. The client calls the meteor server. Meteor.call('xmlDemo');
  2. The meteor server calls the external URL.

If all goes well, the method reads the file contents, and displays the output on the server's console. (i.e. Look at the terminal where you run "meteor")

{ statusCode: 200,
content: '<?xml version="1.0" encoding="ISO8859-1" ?>\r\n<CATALOG>\r\n
(et cetera...)
(et cetera...)
(et cetera...)',
headers:
 { 'content-type': 'text/xml',
   'last-modified': 'Tue, 24 Apr 2012 21:06:45 GMT',
   'accept-ranges': 'bytes',
   etag: '"80a095275e22cd1:0"',
   server: 'Microsoft-IIS/7.5',
   'x-powered-by': 'ASP.NET',
   date: 'Mon, 30 Nov 2015 19:17:26 GMT',
   'content-length': '8167' },
data: null }

The most important thing seen here is that the response from HTTP.call is an object. The "content" field holds the actual data. (This is where I personally got tripped up.)

Fourth, try passing the XML string into xml2js.

Meteor.methods({
    'xmlDemo':function(){
        HTTP.call('GET',
            'http://www.xmlfiles.com/examples/plant_catalog.xml',
            {},
            function(callError,callResponse){
                //console.log('xml',callResponse);
                xml2js.parseString(callResponse.content, function (jsError, jsResult) {
                    console.error('errors',jsError);
                    console.log('xml to js',jsResult);
                });
            }
        );
    }
});

Again, with some luck, you'll see the plants as objects printed out in your terminal. All you have to do now is take that array of objects, loop through it, and store the contents into a collection. Underscore makes that easy (it's built in to Meteor)

_.each(jsResult.CATALOG.PLANT,function(plant){
    Plants.insert(plant);
}

Last you would add few few more fail-safes to report errors. Also xml2js out of the box won't necessarily play nice with how collections work. You need to set a few options.

  • explicitArray:false (by default, a tag like <this>example</this> would turn into {this:["example"]}. In Mongo, that's considered an embedded document, and you'll have trouble.)
  • emptyTag:undefined (by default, an empty tag like <this/> would turn into {this:''}. Personally, I feel that it's easier to let empty things not exist and check if they exist or not rather than check if they exist AND actually have content)

After cleaning up some code of my own, here's the final example

on a /lib/file.js ...

Plants = new Mongo.Collection('plants');

on a /server/file.js ...

Meteor.methods({
    'xmlDemo':function(){
        HTTP.get('http://www.xmlfiles.com/examples/plant_catalog.xml',{},
            function(xmlError,xmlResponse){
                if(xmlError){
                    console.error('xmlError',xmlError);
                }else{
                    xml2js.parseString(xmlResponse.content, {explicitArray:false, emptyTag:undefined}, function (jsError, jsResult) {
                    if(jsError){
                        console.error('xml2js error',jsError);
                    }else{
                        _.each(jsResult.CATALOG.PLANT,function(plant){
                            Plants.insert(plant);
                        }
                    }
                });
            }
        );
    }
});
like image 121
Wray Bowling Avatar answered Oct 27 '22 12:10

Wray Bowling