【问题标题】:How to set the default XML namespace for an XDocument如何为 XDocument 设置默认 XML 命名空间
【发布时间】:2011-02-21 21:32:40
【问题描述】:

如何设置现有 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.

有什么想法吗?

【问题讨论】:

标签: c#


【解决方案1】:

不确定这是否已经在 .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" 语法。

【讨论】:

  • 这基本上是最好的答案。请注意,它通过 XNamespace 上的运算符重载来工作,因此 (ns + name) 返回一个 XName,它指向 完全相同的 XNamespace 对象。很遗憾,您不能使用XName.Get() 来执行此操作。它还对本地名称进行字符串实习,因此您最终不会在内存中使用太多字符串。
  • 我使用这样的函数(之前设置了 xmlns):Func&lt;string, XName&gt; ns = s =&gt; xmlns + s; 然后总是使用ns("MyNodeName")
  • 我同意 Lovell-Smith,这应该是答案。例如,您可以通过执行以下操作从给定的 Xdocument 获取命名空间:XNamespace thenamespace = doc.Root.GetDefaultNamespace(); 并通过执行以下操作获取所有元素的查询:List&lt;XElement&gt; c1 = doc.Elements(thenamespace + "someelementname").Select(c =&gt; (XElement)c).ToList();
【解决方案2】:

Linq to XML 似乎没有为此用例提供 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));
}

【讨论】:

  • 说真的。最常见的用例是如何不受支持的?
  • 这仅在创建文档之前有效吗?当我想更改根节点中现有的xmlns 时,我总是得到相同的值。
【解决方案3】:

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

/// <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】:

    R。 Martinho Fernandes 上面的回答(不会改变现有文档)只需要稍作调整,以便也返回元素值。我没有对此进行过测试,只是在玩 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】:

      修改扩展方法以包含 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】:

        别忘了也复制剩余的属性:

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

        【讨论】:

          【解决方案7】:

          如果您知道您的所有元素都将使用相同的命名空间。例如,如果您正在生成一个 SVG 文档,那么您可以创建一个继承自 XElement 的元素,并在构造函数中显式设置 xlmns 命名空间。

          using System.Xml.Linq;
          
          namespace SVG
          {
              public class SvgElement : XElement
              {
                  private static readonly XNamespace xmlns = "http://www.w3.org/2000/svg";
          
                  /// <inheritdoc />
                  public SvgElement(string name) : base(new XElement(xmlns + name))
                  {
                  }
          
                  /// <inheritdoc />
                  public SvgElement(string name, object content) : this(name)
                  {
                      this.Add(content);
                  }
              }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-03-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-03-13
            相关资源
            最近更新 更多