Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync version of parseString #159

Closed
monya001 opened this issue Oct 9, 2014 · 24 comments
Closed

Sync version of parseString #159

monya001 opened this issue Oct 9, 2014 · 24 comments

Comments

@monya001
Copy link

monya001 commented Oct 9, 2014

How can I run sync version of parseString() function?

@Leonidas-from-XIV
Copy link
Owner

What exactly do you mean by "sync"? xml2js is (unfortunately) currently sync because the AWS bindings depend on being sync.

@monya001
Copy link
Author

Sorry for not being clear.
Currently I run:

parseString(xml, function (err, result) {
    console.dir(result);
});

but instead I want to be able to run

var result = parseString(xml);
console.dir(result);

Is it possible in this or any other way?

@monya001 monya001 reopened this Oct 11, 2014
@Leonidas-from-XIV
Copy link
Owner

No, that would be explicitly blocking behaviour. That is what I want to get away from. You can interact with xml2js using any Async library like async-q or Promise.js.

@jtdevos
Copy link

jtdevos commented Sep 26, 2015

One area where this becomes a problem is karma.conf.js files, which must be synchronous (more info). I work on a java project that specifies it's css/js dependencies in an xml file. It'd be nice use to use xml2js to populate the config.files property, rather than maintain two sets of dependency lists.

Agreed that blocking behavior is usually something to avoid, but this is an area where a synchronous version of parseString() would come in handy.

@Leonidas-from-XIV
Copy link
Owner

Sorry, but that's really not possible. sax.js does not support a synchronous mode of operation that I know of, so I do have to pass it some callbacks to do the parsing.

I'd be gladly proven wrong in a PR, though.

@hcapp01
Copy link

hcapp01 commented May 24, 2016

This PR is over-kill. As Leonidas said just use promise to wrap like this:

async function xml2json(xml) {
    return new Promise((resolve, reject) => {
        parseString(xml, function (err, json) {
            if (err)
                reject(err);
            else
                resolve(json);
        });

    });
}

@HoosierMike
Copy link

HoosierMike commented Aug 1, 2016

Async ain't all it's cracked it up to be. Promises doesn't make it async either. Call/Await isn't available in node.js programming. Why should this call be async anyway, I'm passing it a string, where's the bound.io you are trying to prevent, async is costing time here, not saving it. I don't know why authors of these libraries need to hand cuff us because they think one way is better than another, it's easier to writing a sync version, why not just have both? Constant context switching between threads for such a simple task will cost more time than it will ever save ...

@Leonidas-from-XIV
Copy link
Owner

@HoosierMike It is async because that's the interface that the underlying parser exposes.

@geon
Copy link

geon commented Sep 21, 2016

If the underlying api is async, how comes this works fine:

function parseSync (xml) {

    // Like wtf? Why is this using a callback when it's not async? So pointless.
    var error = null;
    var json = null;
    xml2js.parseString(xml, function (innerError, innerJson) {

        error = innerError;
        json = innerJson;
    });

    if (error) {

        throw error;
    }

    if (!error && !json) {

        throw new Error('The callback was suddenly async or something.');
    }

    return json;
}

Is the underlying API just pretending to be async? Because xml2js.parseString is definately not async. I'ts just using a callback.

@Leonidas-from-XIV
Copy link
Owner

Leonidas-from-XIV commented Sep 21, 2016 via email

@geon
Copy link

geon commented Sep 21, 2016

I didn't intend to mock you, but the api communicates that it actually async, while it apparently is not. Callback != async.

People are asking for a sync version of it, and it is right there.

You mention that you want to get away from blocking behavior, but that just makes no sense. There is no IO going on that would motivate it being async. I suppose the underlying implementation could start it's own process, but that would be extreme overkill for small snippets of xml. If performance is an issue, the api should probably be stream-based anyway.

@Leonidas-from-XIV
Copy link
Owner

The API communicates a callback which is required due to how the underlying SAX parser works, there's not much I can do about it apart from taping a function around which "uncallbacks" it, but that seems unclean to me as well as it implies that SAX.js will always be sync and breaks horribly otherwise.

Blocking behaviour does not have to be IO, runtimes like Erlang also allow preempting on computation which in the case of parsing huge XML files would be nice but I don't see that happening soon.

Streaming APIs are nice but not that great of a match for xml2js, since it is built to output a single parsed object, if streaming is required users can probably just read sub-blocks from their input and send them through xml2js individually.

@super-cache-money
Copy link

Yeah, this is really sad. We all use JSON.parse, and it's sync. I'd wager it's how people would expect this to work by default. It should at least be supported :(

@jtdevos
Copy link

jtdevos commented Dec 3, 2016

Apologies for not replying sooner. I originally wrote a pull request that adds a "parseSync" method to the API, but in retrospect I don't fault @Leonidas-from-XIV for not including it, and I think that the rationale for not changing the API is perfectly valid.

Also, as @geon points out, it's possible to write a sync-style wrapper function. This is generally not considered a smart move (if the underlying library becomes non-blocking someday you're toast), but I imagine it yields better results than sending passive-aggressive notes to an unpaid developer.

@geon
Copy link

geon commented Dec 4, 2016

Actually, you are not toast. You still free to continue using the old version. And as you can see, the code I posted will detect if a newer version is async.

@jtdevos
Copy link

jtdevos commented Dec 5, 2016

Yeah, I meant "toast" in the respect that the sync method would no longer be synchronous. Thanks though.

@davidhq
Copy link

davidhq commented Mar 17, 2017

This looks good

@masaeedu
Copy link

@Leonidas-from-XIV Sorry to reopen this unfortunately charged issue, but would you be willing to take #241 if it was cleaned up and comments addressed? The callback API you have here is kind of weird, because it explodes with an unhandled exception instead of passing the error to the callback I explicitly provide. I was using this with Rx and it took me a while to understand what was going on.

@ghostcode
Copy link

@davidhq thanks

@mtrezza
Copy link

mtrezza commented Aug 26, 2017

For anyone looking for a non-async alternative try fast-xml-parser here.

var fastXmlParser = require('fast-xml-parser');
var jsonObj = fastXmlParser.parse(xmlData);

@ppoulard
Copy link

Within an async function :

    const json = await new Promise((resolve, reject) => {
        parseString(xml, (err, result) => err ? reject(err) : resolve(result) );
    });

@heisian
Copy link

heisian commented Jan 28, 2019

Correct, this library takes a string already presumed to be loaded in memory and returns a transformed string. It is a synchronous operation by nature. The "async" functionality here adds more difficulty in using the library and other parsers have already been benchmarked to be more performant.

What could be considered async is the method of how the function is invoked, i.e., a new process is forked, sent data to parse, and then awaited until that data comes back. But the actual parsing is synchronous.

@wmelton
Copy link

wmelton commented Apr 4, 2019

@hcapp01 - if you use promisify the syntax can be cleaned up further to just use Async/Await.

const xml2js = require('xml2js');
const util = require('util');

const parse = util.promisify(xml2js.parseString);

var asyncParse = async (xml) => {
    try{
      var wait = await parse(xml);
    }catch(e)
    {
      return e;
    }
    return wait;
}

var result = async (xml) => {
    let parsed = await asyncParse(xml);
    console.log(parsed);
}

result('<xml>Hello World</xml>');

@patelravi
Copy link

patelravi commented Aug 29, 2019

Wrapping praseString with promise worked for me.

let parseXml = function (xml) {
        return new Promise((resolve, reject) => {
            require('xml2js').parseString(rawXML, function (err, result) {
                if (err) reject(err)
                else resolve(result)
            });
        });
    }

let result = await parseXml(rawXML);
console.dir(result);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests