-
Notifications
You must be signed in to change notification settings - Fork 607
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
Unexpected Translation of JS Array #428
Comments
Anybody have any input here? I can't imagine I'm the only one needing to correctly encode an XML array. |
For the following sample: const xml2js = require('xml2js');
const headers = {
headers: [
{ header: { '$': { 'method': 'INVITE', name: 'X-Mode', value: 'standard' } } },
{ header: { '$': { 'method': 'INVITE', name: 'X-Prop', value: 'default' } } }
]
}
console.log(new xml2js.Builder().buildObject(headers)); I am seeing the following output: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<headers>
<header method="INVITE" name="X-Mode" value="standard"/>
<header method="INVITE" name="X-Prop" value="default"/>
</headers> Is there any context missing from the question? |
The culprit is here. A new parent node is being created each time the loop is iterated. The root-level array handling doesn't have this problem... |
I can confirm that I have just run into the same issue 🤒 |
Try: headers: { |
Confirmed findings of @ccamarat that the issue exists only when array is not at root-level: const xml2js = require('xml2js');
const headers = {
account: {
headers: [
{ header: { '$': { 'method': 'INVITE', name: 'X-Mode', value: 'standard' } } },
{ header: { '$': { 'method': 'INVITE', name: 'X-Prop', value: 'default' } } }
]
}
}
console.log(new xml2js.Builder().buildObject(headers)); Output: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<account>
<headers>
<header method="INVITE" name="X-Mode" value="standard"/>
</headers>
<headers>
<header method="INVITE" name="X-Prop" value="default"/>
</headers>
</account> Also confirmed that the fix suggested by @dbrooks75 is a suitable workaround: const xml2js = require('xml2js');
const headers = {
account: {
headers: {
header: [
{ '$': { 'method': 'INVITE', name: 'X-Mode', value: 'standard' } },
{ '$': { 'method': 'INVITE', name: 'X-Prop', value: 'default' } }
]
}
}
}
console.log(new xml2js.Builder().buildObject(headers)); Output: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<account>
<headers>
<header method="INVITE" name="X-Mode" value="standard"/>
<header method="INVITE" name="X-Prop" value="default"/>
</headers>
</account> Thanks a lot @dbrooks75! |
@attermann Can this issue be closed? |
While the work around does work, the fact that root-level arrays are interpreted differently than nested arrays seems like a problem. One expects the same input to always produce the same output, regardless of its nesting level. |
@ccamarat OK, I see. What you wrote makes sense, but the problem is that xml2js cannot always produce the same output for the same input regardless of nesting level, because there cannot be multiple XML root elements. More generally: Currently, I think Example:
<y>
<x>a</x>
<x>b</x>
</y> That's fine. But if we want the same output for the same input regardless of its nesting level, then <x>a</x>
<x>b</x> But that's not a well-formed XML document... :( I think |
I think there are two or three deeper questions here:
1. Structural differences between XML elements and JS objectsXML has elements, attributes and text content. JS has objects, arrays and primitive types. xml2js generally represents each XML element as a JS object (and vice versa).
2. To what extent should
|
It looks like In the long run, it would probably be better if In a nutshell: Don't get me wrong, I love xml2js. It's super easy to use, yet super flexible. APIs that combine both of these properties pretty are pretty rare. Good job! |
|
@jcsahnwaldt - great analysis. Agreed that my statement was overly general and you picked it apart well. My impression is that the library is "good enough" for the JavaScript community as a whole, and XML usage in web applications send to be decline declining so devoting energy to making it "perfect" may not be the best use of the maintainers' time. You made the comment "I think buildObject should simply reject most structures that parseString would not produce. A meaningful error message is better than a broken XML result, and the worst is XML that is well-formed but has an unexpected structure, because it makes you think your code is almost correct, when in fact it's quite wrong..." Perhaps this is all that's really needed. It would satisfy me, in any case. |
@jcsahnwaldt I really appreciate your involvement, looking at issues and answering and closing them. Thanks! So let me weight in with some history, some early mistakes and some future directions (hopefully). As such, I am not the original author of Over time, a number of features were contributed, among these the XML builder (the original name I have added a number of options to make the JS object output a little more predictable (like That should not be the case. What I much rather have is to have a fixed, documented semantics (preferably with Flow/TypeScript definitions) on how So yes, I think the parser and builder should be round-tripable (at least in the semantic sense of XML, I don't necessarily care about reproducing the input XML byte for byte), but for this the parser must lose some options. Fortunately, these options are generally not a good idea either and if people want to do that, they can easily do that as a post-processing step after the parser has finished. I have planned to rework the code (since, if you look inside it, it is a mess of deeply nested if statements and mysteriously mutated state) in a more typed and rigid way so it is actually possible to maintain the code, but since I don't actually use Going back to the issue at hand, I think it would make sense if the builder just rejected JS objects that it can't convert to XML instead of generating a hot mess of nonsense that maybe some other poor soul will have to parse. |
I noticed that js2xml (not xml2js) deals with arrays by placing the array items between <item></item> tags Would that be something that makes sense to you? |
@mesieu After looking at js2xml it left me a little bit puzzled: Why does it insert To me the behaviour seems (specified but) arbitrary. I'd rather have a reasonably close correspondence between what xml2js parses to the JS object, which also makes serializing the JS object back to XML more or less unsurprising and does not require adding processing instructions (like "make this array items expand to |
@Leonidas-from-XIV I'm convinced. |
I have a need to encode a js array as xml. For an array like the following:
I am getting this XML:
Instead of the expected:
I suspect I may just be missing something but can’t figure out what it is after reading through the README. Hoping somebody can advise how to make this work.
Thanks!
The text was updated successfully, but these errors were encountered: