-
Notifications
You must be signed in to change notification settings - Fork 221
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
XML namespace imports are not supported #836
Comments
Well, upon further consideration, I think there is a way to replicate what VB does. First, generate a helper class from the namespace imports. This happens once per file, so we need to use something unique to avoid conflicts. Thankfully, a using directive can be used to give a shorter alias. Luckily, one can use Imports <xmlns="http://DefaultNamespace">
Imports <xmlns:ns="http://NewNamespace"> is therefore translated into this montrosity: using XmlImports = XmlImports_3256c4d5_43af_4e4e_897f_71f64e2b775f;
internal static class XmlImports_3256c4d5_43af_4e4e_897f_71f64e2b775f
{
internal static readonly XNamespace _ = "http://DefaultNamespace";
internal static readonly XNamespace ns = "http://NewNamespace";
private static XAttribute[] namespaceAttributes = {
new XAttribute("xmlns", _.NamespaceName),
new XAttribute(XNamespace.Xmlns + "ns", ns.NamespaceName)
};
internal static XElement Apply(XElement x)
{
foreach (var d in x.Descendants()) {
foreach (var n in namespaceAttributes) {
var a = d.Attribute(n.Name);
if(a != null && a.Value == n.Value) {
a.Remove();
}
}
}
x.Add(namespaceAttributes);
return x;
}
} Now the following VB code can be translated in a pretty straightforward way by adding a call to ' XML imports were here
Public Module Module1
Public Sub Main()
' Create element by using the default global XML namespace.
Dim inner = <innerElement/>
' Create element by using both the default global XML namespace
' and the namespace identified with the "ns" prefix.
Dim outer = <ns:outer>
<ns:innerElement></ns:innerElement>
<siblingElement></siblingElement>
<%= inner %>
</ns:outer>
Console.WriteLine(outer)
Console.WriteLine()
' As soon as we use the element in another one, the explicit xmlns attributes get stolen!
Dim wtf = <foobar><%= outer %></foobar>
Console.WriteLine(outer)
End Sub
End Module // Xmlmports hepler goes here
public partial class Program
{
public static void Main()
{
// Create element by using the default global XML namespace.
var inner = XmlImports.Apply(new XElement(XmlImports._ + "innerElement"));
// Create element by using both the default global XML namespace
// and the namespace identified with the "ns" prefix.
var outer = XmlImports.Apply(new XElement(XmlImports.ns + "outer",
new XElement(XmlImports.ns + "innerElement", ""),
new XElement(XmlImports._ + "siblingElement", ""),
inner));
Console.WriteLine(outer);
Console.WriteLine();
// As soon as we use the element in another one, the explicit xmlns attributes get stolen!
var wtf = XmlImports.Apply(new XElement(XmlImports._ + "foobar", outer));
Console.WriteLine(outer);
}
} This way, both produce the same output: <ns:outer xmlns="http://DefaultNamespace" xmlns:ns="http://NewNamespace">
<ns:innerElement></ns:innerElement>
<siblingElement></siblingElement>
<innerElement />
</ns:outer>
<ns:outer xmlns:ns="http://NewNamespace">
<ns:innerElement></ns:innerElement>
<siblingElement xmlns="http://DefaultNamespace"></siblingElement>
<innerElement xmlns="http://DefaultNamespace" />
</ns:outer> Any objections to this appoach? |
No objections to the general direction, it looks like you're onto something. I haven't looked closely yet, but perhaps you can get away with a single general helper class? Each call to apply would pass an array of the attributes stored in that class's field (ideally a short name, or you could add a private expression bodied method in the using class to make it shorter at the callsite) |
Example taken from the Documentation:
Should produce an XElement that prints like this:
I tried reproducing that manually in C# and got the following code:
The only weird thing about that are the two
xmlns
attributes. They are only present on the outermost XElement. But how do we know which one is the outermost? Indeed experimenting with VB.NET reveals, that whenever an XElement is created, the compiler adds code that "steals" the attributes from the inner elements. This can be verified by adding the following code to the example:This causes a mutation on
outer
after the fact, and it loses thexmlns="..."
attribute, resulting in an equivalent, but more verbose representation, where the namespace is specified for each child element.The problem is now, that replicating this behavior would result in very ugly C# code. My suggestion would be to generate the C# code without specifying any additional
xmlns
attributes:Resulting in this XML:
Which causes explicit
xmlns
attributes on each element (except for those equal to their parent). Ignoring which namespace has actually been imported as default, and dropping any aliases declared by imports.Does anyone have a better idea? Have a look at (decompiled from IL) what VB actually does here.
The text was updated successfully, but these errors were encountered: