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

Why do we need a callback for parseString()? #380

Open
zszszsz opened this issue Apr 28, 2017 · 15 comments
Open

Why do we need a callback for parseString()? #380

zszszsz opened this issue Apr 28, 2017 · 15 comments

Comments

@zszszsz
Copy link

zszszsz commented Apr 28, 2017

Since the function parseString is not asynchronous at all ?

(() => {
    var a;
    new require('xml2js').Parser().parseString('<xml>asdf</xml>', (e, r) => { a = r });
    console.log(a);
})();

and the output is:

{ xml: 'asdf' }
@Leonidas-from-XIV
Copy link
Owner

Backward compatibility, since changing the signature breaks all existing depending libraries.

@DoomTay
Copy link

DoomTay commented Jun 2, 2017

Didn't stop the guy behind jsdom. He recently rewrote his API to be callback-less (though still provides a way to use the old API, at least temporarily.)

@jfsiii
Copy link

jfsiii commented Aug 3, 2017

@Leonidas-from-XIV how do you feel about adding parseStringSync which returns the result?

@jeghers
Copy link

jeghers commented Nov 23, 2017

I am trying to use this in a Redux saga, but the callback structure is a showstopper since "yield" won't work inside the callback.

@PhilipDavis
Copy link

Yeah, it's a hassle to not have a synchronous version of parseString. Thanks @zszszsz for a concise workaround.

Anyways, @jeghers See https://redux-saga.js.org/docs/api/#cpsfn-args for using the node-style callback in Redux-Saga

e.g. something like:

const js = yield cps(parseString, xml);

@jeghers
Copy link

jeghers commented Jan 4, 2018 via email

@zszszsz
Copy link
Author

zszszsz commented Jan 4, 2018

@PhilipDavis This parseString is itself synchronous in nature, it is not a workaround actually.

@jeghers you can write you own parseStringSync using the techniques or "workaround" above, exp.

function parseStringSync (str) {
    var result;
    new require('xml2js').Parser().parseString(str, (e, r) => { result = r });
    return result;
}

, and you can avoid using yield in callback

@jeghers
Copy link

jeghers commented Jan 4, 2018 via email

@PhilipDavis
Copy link

PhilipDavis commented Jan 4, 2018

@zszszsz parseString doesn't return the result directly. Hence, your workaround to retrieve it using a closure. What do you call it if not a workaround?

@Leonidas-from-XIV
Copy link
Owner

@jfsiii The parser library that we use, sax.js also provides an async API AFAIK, so unless they provide one there is no clean way to implement parseStringSync in a non-hacky way (like the closure workaround posted here).

@jcsahnwaldt
Copy link
Contributor

jcsahnwaldt commented Jun 10, 2018

I added this function to my copy of parser.coffee. Seems to work quite nicely, and I don't think it's hacky. Should I submit a pull request?

parseStringSync: (str) =>
  res = undefined
  err = undefined
  @on "end", (r) ->
    @reset()
    res = r
  @on "error", (e) ->
    @reset()
    err = e

  @saxParser.write(str).close()

  if err?
    throw err
  else
    return res

I haven't tested it thoroughly yet. The error handling in the original parseString is much more elaborate. I omitted it because I didn't understand it. parseStringSync seems to work fine as it is. When the SAX parser throws an exception, parseStringSync simply lets that exception pass through to the caller, which is fine.

I also omitted the handling of BOMs and empty strings for my tests, but I can easily put that back in.

@zhaoxiongfei
Copy link

zhaoxiongfei commented Jan 29, 2019

The process of parsing xml is CPU exclusive type, So async meaningless. Direct return is a better solution.

@scripting
Copy link

scripting commented Jun 30, 2021

I'm want to build a package on top of xml2js to parse OPML specifically, to make it super easy for people to use. I want to emulate the way JSON.parse and JSON.stringify work. Do I really need to have a callback?

Two possible solutions:

  1. Couldn't the parse routine "just work" if the callback is undefined?
  2. Have another entry-point to the package called parseSync that returns the JavaScript structure for the XML and throws an error if there's an error.

@scripting
Copy link

scripting commented Jun 30, 2021

This is how xml2js.parseStringSync would work.

function parse (xmlltext) {
	var options = {
		explicitArray: false
		};
	return (xml2js.parseStringSync (xmlltext, options));
	}

@suchakraborty
Copy link

I wonder, collectively, how many hours people have wasted stumbling upon this.... During an integration/refactoring, converting json to xml (dont ask), I almost made rewrote everything into promises/async because I assumed this is async. So tally up +1 hr and a 5 minutes debate from my end. 😄 One time rookie mistake - but expensive.

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

10 participants