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

时间:2010-05-20 14:02:10

标签: c#

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

有什么想法吗?

7 个答案:

答案 0 :(得分:49)

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

答案 1 :(得分:47)

似乎Linq to XML没有为此用例提供API(免责声明:我没有深入调查)。如果更改根元素的名称空间,如下所示:

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

只有root元素的名称空间才会更改。所有孩子都有一个明确的空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 :(得分:6)

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

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

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

答案 3 :(得分:3)

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

答案 4 :(得分:2)

修改扩展方法以包含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));
}

现在它适用于我!

答案 5 :(得分:0)

不要忘记复制剩余的属性:

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

答案 6 :(得分:0)

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

xlmns