代码之家  ›  专栏  ›  技术社区  ›  Anthony Faull

如何设置XDocument的默认XML命名空间

c#
  •  42
  • Anthony Faull  · 技术社区  · 15 年前

    如何设置现有XDocument的默认命名空间(以便我可以使用DataContractSerializer对其进行反序列化)。我尝试了以下方法:

    var doc = XDocument.Parse("<widget/>");
    var attrib = new XAttribute("xmlns",
                                "http://schemas.datacontract.org/2004/07/Widgets");
    doc.Root.Add(attrib);
    

    我得到的例外是 The prefix '' cannot be redefined from '' to 'http://schemas.datacontract.org/2004/07/Widgets' within the same start element tag.

    有什么想法吗?

    6 回复  |  直到 10 年前
        1
  •  46
  •   R. Martinho Fernandes    15 年前

    似乎LinqtoXML没有为此用例提供API(免责声明:我没有深入研究)。如果更改根元素的命名空间,如下所示:

    XNamespace xmlns = "http://schemas.datacontract.org/2004/07/Widgets";
    doc.Root.Name = xmlns + doc.Root.Name.LocalName;
    

    只有根元素的名称空间才会更改。所有子级都将有一个显式的空xmlns标记。

    解决方案可以是这样的:

    public static void SetDefaultXmlNamespace(this XElement xelem, XNamespace xmlns)
    {
        if(xelem.Name.NamespaceName == string.Empty)
            xelem.Name = xmlns + xelem.Name.LocalName;
        foreach(var e in xelem.Elements())
            e.SetDefaultXmlNamespace(xmlns);
    }
    
    // ...
    doc.Root.SetDefaultXmlNamespace("http://schemas.datacontract.org/2004/07/Widgets");
    

    或者,如果您喜欢不改变现有文档的版本:

    public XElement WithDefaultXmlNamespace(this XElement xelem, XNamespace xmlns)
    {
        XName name;
        if(xelem.Name.NamespaceName == string.Empty)
            name = xmlns + xelem.Name.LocalName;
        else
            name = xelem.Name;
        return new XElement(name,
                        from e in xelem.Elements()
                        select e.WithDefaultXmlNamespace(xmlns));
    }
    
        2
  •  48
  •   Michael Stum    14 年前

    不确定这是否在.NET 3.5中有效或仅在4中有效,但这对我来说很好:

    XNamespace ns = @"http://mynamespace";
    var result = new XDocument(
        new XElement(ns + "rootNode",
            new XElement(ns + "child",
                new XText("Hello World!")
             )
         )
     );
    

    生成此文档:

    <rootNode xmlns="http://mynamespace">
        <child>Hello World!</child>
    </rootNode>
    

    重要的是始终使用 ns + "NodeName" 语法。

        3
  •  6
  •   Nappy    13 年前

    我有同样的要求,但我想出了一些小的不同:

    /// <summary>
    /// Sets the default XML namespace of this System.Xml.Linq.XElement
    /// and all its descendants
    /// </summary>
    public static void SetDefaultNamespace(this XElement element, XNamespace newXmlns)
    {
        var currentXmlns = element.GetDefaultNamespace();
        if (currentXmlns == newXmlns)
            return;
    
        foreach (var descendant in element.DescendantsAndSelf()
            .Where(e => e.Name.Namespace == currentXmlns)) //!important
        {
            descendant.Name = newXmlns.GetName(descendant.Name.LocalName);
        }
    }
    

    如果您想正确地执行此操作,必须考虑到您的元素可能包含不同名称空间的扩展元素。您不想全部更改它们,只想更改那些默认的名称空间元素。

        4
  •  3
  •   snowcode    12 年前

    r.martinho fernandes在上面回答(这不会改变现有文档),只需要稍作调整,这样元素值也会被返回。我没有在Angst测试过这个,只是在玩LinqPad,抱歉没有提供单元测试。

    public static XElement SetNamespace(this XElement src, XNamespace ns)
    {
        var name = src.isEmptyNamespace() ? ns + src.Name.LocalName : src.Name;
        var element = new XElement(name, src.Attributes(), 
              from e in src.Elements() select e.SetNamespace(ns));
        if (!src.HasElements) element.Value = src.Value;
        return element;
    }
    
    public static bool isEmptyNamespace(this XElement src)
    {
        return (string.IsNullOrEmpty(src.Name.NamespaceName));
    }
    
        5
  •  2
  •   micahhoover    12 年前

    修改扩展方法以包括xelement.value(即叶节点):

    public static XElement WithDefaultXmlNamespace(this XElement xelem, XNamespace xmlns)
    {
        XName name;
        if (xelem.Name.NamespaceName == string.Empty)
            name = xmlns + xelem.Name.LocalName;
        else
            name = xelem.Name;
        if (xelem.Elements().Count() == 0)
        {
            return new XElement(name, xelem.Value);
        }
        return new XElement(name,
                        from e in xelem.Elements()
                        select e.WithDefaultXmlNamespace(xmlns));
    }
    

    现在它对我有用!

        6
  •  0
  •   Phil    10 年前

    不要忘记同时复制其余属性:

      public static XElement WithDefaultXmlNamespace(this XElement xelem, XNamespace xmlns)
        {
            XName name;
            if (xelem.Name.NamespaceName == string.Empty)
                name = xmlns + xelem.Name.LocalName;
            else
                name = xelem.Name;
    
    
            XElement retelement;
            if (!xelem.Elements().Any())
            {
                retelement = new XElement(name, xelem.Value);
            }
            else
             retelement= new XElement(name,
                from e in xelem.Elements()
                select e.WithDefaultXmlNamespace(xmlns));
    
            foreach (var at in xelem.Attributes())
            {
                retelement.Add(at);
            }
    
            return retelement;
        }