Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create XElement with default namespace for children without using XNamespace in all child nodes

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?

like image 327
Barry Kelly Avatar asked Jan 25 '09 17:01

Barry Kelly


3 Answers

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).

like image 129
Barry Kelly Avatar answered Oct 24 '22 15:10

Barry Kelly


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);
        }
    }
like image 28
Julian Lettner Avatar answered Oct 24 '22 15:10

Julian Lettner


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.
like image 21
AnthonyWJones Avatar answered Oct 24 '22 15:10

AnthonyWJones