92

I'm trying to making a weather app that displays the weather and the temperature of many days of the week. I'm currently using openweathermap api for such task, the thing is that the information that I want (that is the date of the weather) only comes in xml format. Since I'm rebuilding it in ES6(ES2015) for academic reasons I wanted to also use the fetch api but since the fetch method parses it, it just delivers an error. so how can i fetch it or mby there is a better way to do it.

let apis = {
    currentWeather: { //get user selected recomendation weather
        api:"http://api.openweathermap.org/data/2.5/forecast/daily?lat=",
        parameters: "&mode=xml&units=metric&cnt=6&APPID=/*api key*/",
        url: (lat, lon) => {
            return apis.currentWeather.api + lat + "&lon=" + lon +
                   apis.currentWeather.parameters
        }
    }
};
function getCurrentLoc() { 
    return new Promise((resolve, reject) =>  navigator.geolocation
                                             .getCurrentPosition(resolve, reject))
}
function getCurrentCity(location) {
    const lat = location.coords.latitude;
    const lon = location.coords.longitude;
    return fetch(apis.currentWeather.url(lat, lon))
    .then(response => response.json())
    .then(data => console.log(data))
}
getCurrentLoc()
.then( coords => getCurrentCity(coords))

5 Answers 5

155

Using native DOMParser getCurrentCity(location) can be written:

function getCurrentCity(location) {
    const lat = location.coords.latitude;
    const lon = location.coords.longitude;
    return fetch(apis.currentWeather.url(lat, lon))
        .then(response => response.text())
        .then(str => new window.DOMParser().parseFromString(str, "text/xml"))
        .then(data => console.log(data));
}
Sign up to request clarification or add additional context in comments.

5 Comments

So the DOMParser prototype only have one documented member parseFromString? I thought there were also parseFromStream, parseFromBlob, etc…
Steve Griffith has a great video that shows him using fetch to get XML (much like the code above). He has the code from his video in a gist.
as a side note: I wouldn't combine "great" and "video" into one sentence, when speaking about programming.
If DOMParser is part of window(and as the name indicates) it must be only valid for client-side JavaScript. What would you do in other cases, e.g. in Node.js?
use a library, fast-xml-parser for example
18

I guess the error is coming from this function: response => response.json() since the response is not a valid JSON object (it's XML).

As far as I know, there is no native XML parser for fetch, but you can handle the response as text and use a third party tool to do the actual parsing, for example jQuery has a $.parseXML() function.

It will look something like:

function getCurrentCity(location) {
    const lat = location.coords.latitude;
    const lon = location.coords.longitude;
    return fetch(apis.currentWeather.url(lat, lon))
        .then(response => response.text())
        .then(xmlString => $.parseXML(xmlString))
        .then(data => console.log(data))
}

2 Comments

I can confirm that there's no native XML parser for fetch. See developer.mozilla.org/en-US/docs/Web/API/Response#Methods.
The jQuery function for parsing XML is short and sweet, and uses DOMParser(), as in @JukkaP's answer: github.com/jquery/jquery/blob/…
12

It is possible to use the npm xml-js library and node-fetch to do this in Node.js, for those who want to test this out in the Node REPL.

First off we install the two modules xml-js and node-fetch with:

npm install xml-js --save npm install node-fetch --save

to store these two packages into package.json. Now over to our problem at hand - how to work with XML data returned from an API.

Consider the following example fetching a particular weather station in Norway:

const fetch = require('node-fetch');
const convert = require('xml-js');
let dataAsJson = {};

fetch('http://eklima.met.no/metdata/MetDataService?invoke=getStationsProperties&stations=68050&username=')
    .then(response => response.text())
    .then(str => {
        dataAsJson = JSON.parse(convert.xml2json(str))
    })
    .then(() => {
        console.log('Station id returned from the WS is:' + 
            `${dataAsJson.elements[0].elements[0].elements[0].elements[0].elements[0].elements
                .filter(obj => { return obj.name == 'stnr'; })[0].elements[0].text} Expecting 68050 here!`
        );
    });

We now have got a variable that is actually parsed into a JSON object from the XML data using convert's xml2json method and using JSON.parse. If we want to print out the object, we can use JSON.stringify to turn the JSON object into a string. The retrieval of the station id in this code just shows the need to scan through an object graph deep for a given key, since turning XML into Json gives often even deeper object graphs, since the wrapping XML elements are always at the top of the "XML object JSON-graph". There are some tips around deep searching object graphs that are deep to look for a key, like obj-traverse library on GitHub

1 Comment

This does not give the expected key-value JSON, but a convoluted one with lots of extra keys.
1

This worked in my angular app

import * as xml2js from 'xml2js';

url = MY_URL;

ngOnInit(): void {
 this.getData();
}

getData(): void {
  fetch(MY_URL)
    .then(response => response.text())
    .then(data => {
      let parseString = xml2js.parseString;
      parseString(data, function (err, result) {
        console.log(result);
        console.error(err);
      });
    });
}

Comments

0

Usage in Node.js environment

Disclaimer: this is an extended answer of the original accepted answer.

As @Jinxmcg suggest, to parse the message in node you can use lib like fast-xml-parser.

  1. Install fast-xml-parser package
yarn add fast-xml-parser
  1. Usage
const { XMLParser } = require("fast-xml-parser");

function getCurrentCity(location) {
    const lat = location.coords.latitude;
    const lon = location.coords.longitude;
    
    return fetch(apis.currentWeather.url(lat, lon))
        .then(response => response.text())
        .then(text => new XMLParser().parse(text))
        .then(data => console.log(data));
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.