I'm trying to use System.Xml.Linq to create XHTML documents. Thus, the vast majority of the nodes in my trees ought to use this namespace:
http://www.w3.org/1999/xhtml
I can create XElement nodes scoped to this namespace easily enough, using an XNamespace, like this:
XNamespace xhtml = "http://www.w3.org/1999/xhtml";
// ...
new XElement(xhtml + "html", // ...
However, I don't want to have to make an XNamespace available throughout all the code that creates HTML nodes, and have to prefix every single XElement (and XAttribute) name I create accordingly.
The XML text format itself takes this requirement into account, and permits setting a default namespace in an ancestor which is inherited by descendants, using the reserved xmlns attribute. I'd like to do something similar using System.Xml.Linq.
Is this possible?
I've decided to use a static class called XHtml, that looks like this:
public static class XHtml
{
static XHtml()
{
Namespace = "http://www.w3.org/1999/xhtml";
}
public static XNamespace Namespace { get; private set; }
public static XElement Element(string name)
{
return new XElement(Namespace + name);
}
public static XElement Element(string name, params object[] content)
{
return new XElement(Namespace + name, content);
}
public static XElement Element(string name, object content)
{
return new XElement(Namespace + name, content);
}
public static XAttribute Attribute(string name, object value)
{
return new XAttribute(/* Namespace + */ name, value);
}
public static XText Text(string text)
{
return new XText(text);
}
public static XElement A(string url, params object[] content)
{
XElement result = Element("a", content);
result.Add(Attribute("href", url));
return result;
}
}
This seems to be the cleanest way of doing things, particularly as I can then add in convenience routines, such as the XHtml.A method (not all of my class is shown here).
I took the recursively rewriting path. You do not really have to 'reconstruct' the tree. You can just swap out the node names (XName).
private static void ApplyNamespace(XElement parent, XNamespace nameSpace)
{
if(DetermineIfNameSpaceShouldBeApplied(parent, nameSpace))
{
parent.Name = nameSpace + parent.Name.LocalName;
}
foreach (XElement child in parent.Elements())
{
ApplyNamespace(child, nameSpace);
}
}
The problem is the the XName used to create the XElement needs to specify the correct namespace. What I would be tempted to do is create a static class like this:-
public static class XHtml
{
public static readonly XNamespace Namespace = "http://www.w3.org/1999/xhtml";
public static XName Html { get { return Namespace + "html"; } }
public static XName Body { get { return Namespace + "body"; } }
//.. other element types
}
Now you can build a xhtml doc like this:-
XDocument doc = new XDocument(
new XElement(XHtml.Html,
new XElement(XHtml.Body)
)
);
An alternative approach to that static class would be:-
static class XHtml
{
public static readonly XNamespace Namespace = "http://www.w3.org/1999/xhtml";
public static readonly XName Html = Namespace + "html";
public static readonly XName Body = Namespace + "body";
}
This has the downside of instancing all the possible XName regardless of whether you use them but the upside is the conversion of Namespace + "tagname" only happens once. I'm not sure this conversion would be optimised out otherwise. I am sure that XNames are only instanced once:-
XNamepace n = "http://www.w3.org/1999/xhtml";
XNames x = n + "A";
XName y = n + "A";
Object.ReferenceEquals(x, y) //is true.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With