0

I am creating an XML file while looping through an array using underscore each. In that loop, I need to call an external resource to retrieve a value to write an XML element for this specific item in the array.

I'm am confused as how to retrieve the value from the external resource while maintaining the order of the loop so that the value is included in the correct item in the array.

I'm returning a Promise in the function that retrieves the value, but this is clearly incorrect.

import Promise from 'bluebird';
import XMLWriter from 'xml-writer';
import _ from 'underscore';
import request from 'request';


class Tester {
  writeXml() {
    let stores = [
      {StoreId: 1, LocationId: 110},
      {StoreId: 14, LocationId: 110},
      {StoreId: 15, LocationId: 110},
    ];

    let xw = new XMLWriter();
    xw.startDocument();
    xw.startElement('Stores');

    // Loop through all the stores to write the XML file.
    _.each(stores, (s) => {
          xw.startElement('Store')
          .startElement('StoreId').text(s.StoreId).endElement()
          .startElement('LocationId').text(s.LocationId).endElement()

          // Need to call an external resource here to get the
          // store code, but unsure how to do this.
          this.getStoreCode(s.LocationId, s.StoreId).then((storeCode) => {
            xw.startElement('StoreCode').text(storeCode).endElement();
          });

          xw.endElement();
      });

    xw.endDocument();
    console.log(xw.toString());
  }

  getStoreCode(locationId, storeId) {
    return new Promise((resolve, reject) => {
      let apiUrl = 'http://127.0.0.1:3000/api/v1/stores?filter' +
        '[where][locationId]=' + locationId +
        '&filter[where][storeId]=' + siteId + '&filter[fields][storeCode]=true';

      request(apiUrl, (error, response, body) => {
        if (!error) {
          let result = JSON.parse(body);
          return resolve(result[0].storeCode);
        } else {
          return reject(error);
        }
      });
    });
  }
}

new Tester().writeXml();
3
  • I guess, you need to load your resource first and then write the xml structure Commented Mar 10, 2016 at 15:04
  • I agree with webduvet. Load all your resources into JS objects first and then, once all the promises are resolved, write the xml. Commented Mar 10, 2016 at 15:12
  • Have a look at Promise.all. You should use map instead of forEach Commented Mar 10, 2016 at 15:15

1 Answer 1

2

Untested, but this is the way to go (or, more modestly, one way to go).

writeXml(stores) {
  let xw = new XMLWriter();
  xw.startDocument();
  xw.startElement('Stores');

  return Promise.map(stores, (store) => {
    return this.getStoreCode(store.LocationId, store.StoreId).then((storeCode) => {
      store.StoreCode = storeCode;
      return store;
    });
  }).each((storeWithStoreCode) => {
    xw.startElement('Store');
    _.each(['StoreId', 'LocationId', 'StoreCode'], (prop) => {
      xw.startElement(prop).text(storeWithStoreCode[prop]).endElement();
    });
    xw.endElement();
  }).then(() => {
    xw.endDocument();
    return xw;
  });
}

and

writeXml([
  {StoreId: 1, LocationId: 110},
  {StoreId: 14, LocationId: 110},
  {StoreId: 15, LocationId: 110},
]).then((xw) => {
    console.log(xw.toString());
});

In English, this uses Promise.map() to project plain store objects into Promises for store objects that have a StoreCode property.

After that, .each() of those is written into the XML writer, in sequence.

In the end the whole chain resolves to the XML writer object itself, you might want to change that into a different result.

Functions that use promises should return a promise (or invoke some kind of callback), so that calling code has a chance of knowing when that function is done.

Sign up to request clarification or add additional context in comments.

4 Comments

Is it allowed to omit the xw.endElement() matching xw.startElement('Stores')?
@Roamer-1888 I'm not sure. Intuitively I would have expected that endDocument() implicitly ends all open elements, but I've not tested it (or looked it up).
@duffn Don't forget error management, where appropriate. It's not ideal to leave error behavior undefined.
@Tomalak, the examples here indicate just that. Nice work BTW.

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.