fix: infer/generate prefixes for unprefixed namespaced attribute nodes.#903
fix: infer/generate prefixes for unprefixed namespaced attribute nodes.#903hungarian-notation wants to merge 15 commits intoxmldom:masterfrom
Conversation
| const n1_test = doc.createElementNS(n1, 'test'); | ||
| n1_test.setAttribute('xmlns', n1); | ||
| n1_test.setAttributeNS(n1, 'bar', 'valx'); | ||
| expect(n1_test.toString()).toBe('<test xmlns="' + n1 + '" bar="valx"/>'); |
There was a problem hiding this comment.
As discussed in #901, the unprefixed bar="..." attribute the original test is looking for isn't actually in the n1 namespace, it's in no namespace at all.
There was a problem hiding this comment.
That's interesting. Since we had a test for it, I think it count's as "expected behavior".
So changing that behavior should be considered a breaking change, right?
There was a problem hiding this comment.
xmldom is already parsing unprefixed attributes correctly, it's only mishandling them in XMLSerializer. What this means is that getAttributeNS and setAttributeNS are asymmetric for unprefixed attributes if you serialize/de-serialize the document in-between.
import { DOMParser, XMLSerializer } from "@xmldom/xmldom";
(() => {
const document = new DOMParser().parseFromString("<root/>", "text/xml");
const attributeNamespace = "uri:default";
const attributeLocalName = "example";
const attributeValue = "value";
// setAttributeNS
document.documentElement.setAttributeNS(
attributeNamespace,
attributeLocalName,
attributeValue
);
const serialized = new XMLSerializer().serializeToString(document);
const deserialized = new DOMParser().parseFromString(serialized, "text/xml");
// getAttributeNS
const deserializedAttributeValue = deserialized.documentElement.getAttributeNS(
attributeNamespace,
attributeLocalName
);
console.log(" expected:", attributeValue);
console.log(" actual:", deserializedAttributeValue);
})();Without this patch, xmldom gives null for deserializedAttributeValue, which I'd argue is a bug. This test codifies this bug.
Both Gecko and Chromium give the expected result of "value" instead.
There was a problem hiding this comment.
const n1_test = doc.createElementNS(n1, 'test');
n1_test.setAttribute('xmlns', n1);
n1_test.setAttributeNS(n1, 'bar', 'valx');
expect(n1_test.toString()).toBe('<test xmlns="' + n1 + '" bar="valx"/>');What this code is doing is asking xmldom to create an unrepresentable DOM state and then to emit XML that describes a different state entirely. The first argument being passed to setAttributeNS could be literally anything here, since the attribute the serializer is emitting is not in whatever namespace the user has specified.
In fact, the bugginess becomes much more obvious if the attribute namespace doesn't match the default namespace.
(() => {
const defaultNamespace = "uri:default-namespace";
const document = new DOMParser().parseFromString(`<root xmlns="${defaultNamespace}"/>`, "text/xml");
const attributeNamespace = "uri:default";
const attributeLocalName = "example";
const attributeValue = "value";
document.documentElement.setAttributeNS(
attributeNamespace,
attributeLocalName,
attributeValue
);
const serialized = new XMLSerializer({ }).serializeToString(document);
console.log(serialized);
})();Currently, this emits:
<root xmlns="uri:default-namespace" xmlns="uri:default" example="value" xmlns="uri:default-namespace"/>Which is obviously incorrect. Not only is this not well-formed XML, and not only is example still not in either namespace, but now it's ambiguous as to what namespace the root element is in. It would be odd for an implementation to respect the second of the three xmlns attributes, but it would be equally valid as respecting either the first or third.
There was a problem hiding this comment.
As an alternative, the more rational behavior could be hidden behind an options flag on the XMLSerializer object so as not to break any real-world code that is relying on the buggy behavior.
If that's preferable, you can reject this pull and I'll open a new one along those lines.
There was a problem hiding this comment.
Thank you for the decent explaination. Makes total sense that we should consider this a bug, and not a breaking change.
Are you willing to check if the same bug exists in the latest 0.8.* version, which we provide as "kind of an LTS if possible"? And if it's present how hard it would be to backport your additions there? This request is of course completely independent from this PR.
There was a problem hiding this comment.
It's almost directly applicable to 0.8.x as well, just needed a few tweaks. See #904
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #903 +/- ##
==========================================
+ Coverage 95.12% 95.18% +0.05%
==========================================
Files 8 8
Lines 2196 2220 +24
Branches 577 585 +8
==========================================
+ Hits 2089 2113 +24
Misses 107 107 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
karfau
left a comment
There was a problem hiding this comment.
Thx for your contribution. So far I only had a first quick look and left some comments.
Please take care of them, I'll need some time to digest this PR in detail.
this time without tracking a bunch of junk in the coverage folder
|
Sorry for the delay in processing this. I still have one very busy week in front of me, but afterwards I should be able to find the time for this topic. |
|
Hey @hungarian-notation I finally managed to look into it closer and still have some doubts: I'm still having a hard time wrapping my head around the topic, but I know that PS: I'm not in reach of any computer and very likely also only with limited internet connection on the phone until 4th of August, so I will only be able to reply afterwards. The rest of the message is only relevant, if those operations need to be possible and the fix can only ever be applied as part of serialization: I think I detected some flaws in your implementation of
I have refactored the method accordingly, started adding some basic tests which left me puzzled how to reproduce the different cases. |
|
Here is a failing test The serialized result is: Notice the duplicate attribute. |
|
I tried to work on the serializer but ended up completely rewriting it because I disliked the monolithic approach, and having it all squished into dom.js was quite cumbersome to work on. I had to fiddle with it quite a bit to match the expected behavior of the old serializer (attribute ordering, self closing tags etc) and managed to end up with only 1 failing test: But I would argue that what this test expects is wrong. In any case I recognize this would be a big change so I am not going to push here or a new PR if this is not the direction you want to go in. |
Small patch that implements the changes described in #901
The changes broke existing test XML Namespace Parse > should ignore default prefix xml attribute , which relied on the buggy behavior. I have amended that test accordingly.