Как создать XElement с пространством имен по умолчанию для дочерних элементов без использования XNamespace во всех дочерних узлах

Я пытаюсь использовать System.Xml.Linq для создания документов XHTML. Таким образом, подавляющее большинство узлов в моих деревьях должны использовать это пространство имен:

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

Я могу достаточно легко создать узлы XElement в этом пространстве имен, используя XNamespace, например:

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

Однако я не хочу, чтобы XNamespace был доступен во всем коде, который создает узлы HTML, и должен добавлять префикс к каждому отдельному имени XElementXAttribute), которое я создаю соответствующим образом.

Сам текстовый формат XML учитывает это требование и позволяет установить пространство имен по умолчанию в предке, которое наследуется потомками, используя зарезервированный атрибут xmlns. Я хотел бы сделать что-то подобное, используя System.Xml.Linq.

Это возможно?


person Barry Kelly    schedule 25.01.2009    source источник


Ответы (3)


Я решил использовать статический класс 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 (здесь показан не весь мой класс).

person Barry Kelly    schedule 07.02.2009
comment
Это решение выглядит тяжелым и пугающим, но на самом деле оно красивое, легкое, расширяемое, делает ваш код короче и защищает от опечаток в именах элементов или атрибутов. Вау! - person Stéphane Gourichon; 13.05.2016
comment
Я просто понял, почему у него есть все эти преимущества. Хотя в вопросе говорилось только о том, что я хочу автоматические пространства имен, простой и разумный поступок заключается в том, что я хотел бы использовать некоторые удобные ярлыки, которые инкапсулируют некоторые базовые знания HTML, из которых эффект пространства имен является просто приятным побочным продуктом. - person Stéphane Gourichon; 13.05.2016

Я пошел по пути рекурсивной перезаписи. Вам действительно не нужно «реконструировать» дерево. Вы можете просто поменять местами имена узлов (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);
        }
    }
person Julian Lettner    schedule 28.05.2010

Проблема заключается в том, что XName, используемый для создания XElement, должен указывать правильное пространство имен. У меня возникло бы искушение создать статический класс следующим образом:

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 независимо от того, используете ли вы их, но преимущество заключается в том, что преобразование пространства имен + «имя тега» происходит только один раз. Я не уверен, что в противном случае это преобразование было бы оптимизировано. Я уверен, что XNames создаются только один раз: -

XNamepace n = "http://www.w3.org/1999/xhtml";
XNames x = n + "A";
XName y = n + "A";
Object.ReferenceEquals(x, y) //is true.
person AnthonyWJones    schedule 25.01.2009
comment
Третий подход состоит в том, чтобы переписать поддерево, html-узлом вниз, перед добавлением в XDocument в корень. Я знаю об этих других способах решения проблемы, мне было интересно, есть ли способ обойти это. Наверное, нет. - person Barry Kelly; 25.01.2009
comment
Эм... а? html-узел не работает? Я не понимаю. Вам по-прежнему нужно использовать XNames с пространством имен xhtml. - person AnthonyWJones; 25.01.2009
comment
Энтони — переписать дерево, чтобы включить пространство имен XHTML в имена, которые не входят в пространство имен, т. е. проверка XName.Namespace на XNamespace.None; под узлом html я имею в виду корневой узел XHTML-документов. - person Barry Kelly; 26.01.2009
comment
Перезапись = рекурсивная реконструкция всего дерева с новыми значениями XName в конструкторах, составленными из имени старого узла с пространством имен XHTML. - person Barry Kelly; 27.01.2009