如何在不使用XNamespace的情况下为子节点创建默认命名空间的XElement?

9

我正在尝试使用System.Xml.Linq来创建XHTML文档。因此,我的树中绝大部分节点都应该使用这个命名空间:

http://www.w3.org/1999/xhtml

我可以很容易地使用 XNamespace 创建限定于该命名空间的 XElement 节点,就像这样:

XNamespace xhtml = "http://www.w3.org/1999/xhtml";
// ...
new XElement(xhtml + "html", // ...

然而,我不想在创建 HTML 节点的所有代码中都要使用 XNamespace,并且必须相应地为我创建的每个单独的 XElement(和 XAttribute)名称添加前缀。
XML 文本格式本身考虑了这个要求,并允许在祖先中设置默认命名空间,该命名空间由后代继承,使用保留的 xmlns 属性。我想使用 System.Xml.Linq 做类似的事情。
这是可能的吗?
3个回答

5

我决定使用一个名为XHtml的静态类,它看起来像这样:

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;
    }
}

这似乎是做事情最干净的方式,特别是我可以添加方便的例程,例如XHtml.A方法(这里没有展示我的类的全部内容)。


这个解决方案看起来很沉重和可怕,但实际上它是很好的,轻量级的,可扩展的,可以使你的代码更短并保护元素或属性名称中的拼写错误。哇! - Stéphane Gourichon
我刚明白了它为什么有这些优点。虽然问题只是说“我想要自动命名空间”,但简单和明智的做法是“我想要一些方便的快捷方式,封装一些基本的HTML知识”,其中命名空间效果只是一个很好的副产品。 - Stéphane Gourichon

3

我选择了递归重写的方法。你不需要真正地“重构”树。你只需交换节点名称(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);
        }
    }

1
问题在于用于创建XElement的XName需要指定正确的命名空间。我会尝试创建一个静态类,像这样:-
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
}

现在你可以像这样构建一个 XHTML 文档:

XDocument doc = new XDocument(
    new XElement(XHtml.Html,
        new XElement(XHtml.Body)
    )
);

对于那个静态类的另一种方法是:

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";
}

这种方法的缺点是会实例化所有可能的XName,无论您是否使用它们,但好处是Namespace +“tagname”的转换只会发生一次。否则,我不确定这种转换是否会被优化掉。我确信XNames只会被实例化一次:

XNamepace n = "http://www.w3.org/1999/xhtml";
XNames x = n + "A";
XName y = n + "A";
Object.ReferenceEquals(x, y) //is true.

第三种方法是在将子树添加到根XDocument之前重新编写html-node下的子树。我知道其他解决问题的方法,但我想知道是否有绕过它的方法。我猜可能没有。 - Barry Kelly
嗯...啥?html-node down?我不明白。你仍然需要使用具有xhtml命名空间的XNames。 - AnthonyWJones
Anthony - 重写树结构,将未受命名空间限制的名称包含在XHTML命名空间中,即检查XName.Namespace是否为XNamespace.None;通过html节点,我指的是XHTML文档的根节点。 - Barry Kelly
重写 = 递归重构整个树,使用构造函数中的新XName值,由旧节点名称与XHTML命名空间组成。 - Barry Kelly

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接