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?
First you will need to install a couple of packages.
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.
Meteor.call('xmlDemo');
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.
<this>example</this>
would turn into {this:["example"]}
. In Mongo, that's considered an embedded document, and you'll have trouble.)<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);
}
}
});
}
);
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With