Description
Description
Note: this post has been edited to summarize the whole discussion.
I'm experiencing inconsistent behavior regarding XML namespace prefixing. When I create an XElement via new XElement()
the code behaves differently from when I use XElement.Parse()
to parse the equivalent XML structure.
See code sample below. The first assert succeeds. The second assert fails.
The root
element knows about the namespace prefixing. After adding both elements to it, only the one created with the constructor gets prefixed properly. See string representations of each object as they are at the end of the Fact
method.
original: <mn:p xmlns:mn="http://my-namespace.com/">text</mn:p>
deserialized: <p xmlns="http://my-namespace.com/">text</p>
root:
<html xmlns:mn="http://my-namespace.com/">
<mn:p>text</mn:p>
<p xmlns="http://my-namespace.com/">text</p>
</html>
namespace MyNamespace
{
using System.Xml.Linq;
using Xunit;
public class XmlParsing
{
private const string Prefix = "mn";
private static readonly XNamespace Namespace = "http://my-namespace.com/";
[Fact]
public void Fact()
{
var root = new XElement("html", new XAttribute(XNamespace.Xmlns + Prefix, Namespace));
var original = new XElement(Namespace + "p", "text");
var deserialized = XElement.Parse(original.ToString());
// original: [<p xmlns="http://my-namespace.com/">text</p>]
// deserialized: [<p xmlns="http://my-namespace.com/">text</p>]
Assert.Equal(original.ToString(), deserialized.ToString()); // succeeds
root.Add(original);
root.Add(deserialized);
// original: [<mn:p xmlns:mn="http://my-namespace.com/">text</mn:p>]
// deserialized: [<p xmlns="http://my-namespace.com/">text</p>]
Assert.Equal(original.ToString(), deserialized.ToString()); // fails
}
}
}
I would expect the namespace prefixing to work identical no matter how the XElement
is created.
Using XDocument
instead, leads to the same error.
public class XmlParsing
{
private const string Prefix = "mn";
private static readonly XNamespace Namespace = "http://my-namespace.com/";
[Fact]
public void Fact()
{
var root = new XElement("html", new XAttribute(XNamespace.Xmlns + Prefix, Namespace));
var document = new XDocument();
document.Add(root);
var original = new XElement(Namespace + "p", "text");
var deserialized = XDocument.Parse(original.ToString()).Root;
// original: [<p xmlns="http://my-namespace.com/">text</p>]
// deserialized: [<p xmlns="http://my-namespace.com/">text</p>]
Assert.Equal(original.ToString(), deserialized.ToString()); // succeeds
root.Add(original);
root.Add(deserialized);
// original: [<mn:p xmlns:mn="http://my-namespace.com/">text</mn:p>]
// deserialized: [<p xmlns="http://my-namespace.com/">text</p>]
Assert.Equal(original.ToString(), deserialized.ToString()); // fails
}
}
Would be nice to add some justification why do you think this behavior is better and comments with what are the values on each assert
My expectation would be that theXElement
implementation behaves exactly the same. Independent of how theXElement
is created. Be it using the constructor or theXElement.Parse()
method.
I also created a gist containing above code to demonstrate the issue.
Configuration
Above code is running in a netcoreappe3.1 with C# 8.0 nullable enabled on a Windows 10 Pro x64
Regression?
Other information
- Do you know of any workarounds?
The only work around I found is to explicitly add thenew XAttribute(XNamespace.Xmlns + Prefix, Namespace)
attribute to the original XElement. Since the xml I'm trying to parse is not 100% under our control, this will not work in the real world scenario.
Doesn't really work, as the resulting xml will contain unnecessary namespace prefix declarations