LINQ to XML - 如何修复根元素中的默认命名空间

时间:2013-02-19 12:57:49

标签: c# linq-to-xml xml-namespaces

考虑生成以下XML结构,该结构具有2个带前缀的命名空间:

XNamespace ns1 = "http://www.namespace.org/ns1";
const string prefix1 = "w1";
XNamespace ns2 = "http://www.namespace.org/ns2";
const string prefix2 = "w2";

var root = 
    new XElement(ns1 + "root", 
        new XElement(ns1 + "E1"
            , new XAttribute(ns1 + "attr1", "value1")
            , new XAttribute(ns2 + "attr2", "value2"))
        , new XAttribute(XNamespace.Xmlns + prefix2, ns2)
        , new XAttribute(XNamespace.Xmlns + prefix1, ns1)
    );

它生成以下XML结果(很好):

<w1:root xmlns:w2="http://www.namespace.org/ns2" xmlns:w1="http://www.namespace.org/ns1">
  <w1:E1 w1:attr1="value1" w2:attr2="value2" />
</w1:root>

当我尝试通过注释掉其XML声明来将ns1从带前缀的命名空间更改为默认命名空间时出现问题,如:

var root = 
    new XElement(ns1 + "root", 
        new XElement(ns1 + "E1"
            , new XAttribute(ns1 + "attr1", "value1")
            , new XAttribute(ns2 + "attr2", "value2"))
        , new XAttribute(XNamespace.Xmlns + prefix2, ns2)
        //, new XAttribute(XNamespace.Xmlns + prefix1, ns1)
    );

产生:

<root xmlns:w2="http://www.namespace.org/ns2" xmlns="http://www.namespace.org/ns1">
  <E1 p3:attr1="value1" w2:attr2="value2" xmlns:p3="http://www.namespace.org/ns1" />
</root>

请注意rootE1中的重复名称空间定义以及p3E1前缀为public static void FixDefaultXmlNamespace(this XElement xelem, XNamespace ns) { if(xelem.Parent != null && xelem.Name.Namespace == ns) { if(xelem.Attributes().Any(x => x.Name.Namespace == ns)) { var attrs = xelem.Attributes().ToArray(); for (int i = 0; i < attrs.Length; i++) { var attr = attrs[i]; if (attr.Name.Namespace == ns) { attrs[i] = new XAttribute(attr.Name.LocalName, attr.Value); } } xelem.ReplaceAttributes(attrs); } } foreach (var elem in xelem.Elements()) elem.FixDefaultXmlNamespace(ns); } 的属性。我怎样才能避免这种情况发生?如何强制在根元素中声明默认命名空间?

相关问题

我研究了这个问题:How to set the default XML namespace for an XDocument

但是建议的答案替换了没有定义任何名称空间的元素的名称空间。在我的示例中,元素和属性已正确设置其命名空间。

我尝试了什么

基于过多的试验和错误,在我看来,属性不直接位于根节点下,其中属性及其直接父元素都具有与默认命名空间相同的命名空间;需要删除属性的名称空间!!!

基于此,我定义了以下扩展方法,该方法遍历生成的XML的所有元素并执行上述操作。到目前为止,在我的所有样本中,这种扩展方法成功地解决了这个问题,但这并不一定意味着有人不能为它生成一个失败的例子:

<root xmlns:w2="http://www.namespace.org/ns2" xmlns="http://www.namespace.org/ns1">
  <E1 attr1="value1" w2:attr2="value2" />
</root>

此扩展方法为我们的问题生成以下XML,这是我想要的:

{{1}}

但是我不喜欢这种解决方案,主要是因为它很贵。我觉得我在某个地方错过了一个小小的场景。有什么想法吗?

2 个答案:

答案 0 :(得分:0)

我找到了一些适合你的东西,来自Cuts in a Nutshell一书:

您也可以将名称空间分配给属性。它的主要区别在于它始终如一 需要一个前缀。例如:

<customer xmlns:nut="OReilly.Nutshell.CSharp" nut:id="123" />

另一个区别是非限定属性始终具有空命名空间: 它永远不会从父元素继承默认命名空间。

所以考虑到你想要的输出,我做了一个简单的检查。

        var xml = @"<root xmlns:w2=""http://www.namespace.org/ns2"" xmlns=""http://www.namespace.org/ns1"">
                <E1 attr1=""value1"" w2:attr2=""value2"" />
            </root>";

        var dom = XElement.Parse(xml);
        var e1 = dom.Element(ns1 + "E1");

        var attr2 = e1.Attribute(ns2 + "attr2");
        var attr1 = e1.Attribute(ns1 + "attr1");
        // attr1 is null !

        var attrNoNS = e1.Attribute("attr1");
        // attrNoNS is not null

因此,简而言之,attr1没有默认命名空间,但是有一个空命名空间。

这是你想要的吗?如果是,只需创建没有命名空间的attr1 ......

new XAttribute("attr1", "value1")

答案 1 :(得分:0)

引自here

  

属性不被视为其父元素的子元素。属性永远不会继承其父元素的名称空间。因此,如果属性具有正确的名称空间前缀,则该属性仅在名称空间中。属性永远不能位于默认命名空间中。

here

  

默认名称空间声明适用于其范围内的所有未加前缀的元素名称。默认名称空间声明不直接应用于属性名称;对无前缀属性的解释由它们出现的元素决定。

似乎LINQ-to-XML的这种奇怪行为植根于标准。因此,每当添加新属性时,必须将其名称空间与其范围内活动的父项的默认名称空间进行比较。我使用此扩展方法添加属性:

public static XAttribute AddAttributeNamespaceSafe(this XElement parent, 
         XName attrName, string attrValue, XNamespace documentDefaultNamespace)
{
    if (newAttrName.Namespace == documentDefaultNamespace)
        attrName = attrName.LocalName;

    var newAttr = new XAttribute(attrName, attrValue);
    parent.Add(newAttr);
    return newAttr;
}

并使用此扩展方法检索属性:

public static XAttribute GetAttributeNamespaceSafe(this XElement parent, 
        XName attrName, XNamespace documentDefaultNamespace)
{
    if (attrName.Namespace == documentDefaultNamespace)
        attrName = attrName.LocalName;
    return parent.Attribute(attrName);
}

或者,如果您手头有XML结构并希望修复已添加到属性的名称空间,请使用以下扩展方法来解决此问题(与问题中概述的略有不同):

public static void FixDefaultXmlNamespace(this XElement xelem, 
        XNamespace documentDefaultNamespace)
{
    if (xelem.Attributes().Any(x => x.Name.Namespace == documentDefaultNamespace))
    {
        var attrs = xelem.Attributes().ToArray();
        for (int i = 0; i < attrs.Length; i++)
        {
            var attr = attrs[i];
            if (attr.Name.Namespace == documentDefaultNamespace)
            {
                attrs[i] = new XAttribute(attr.Name.LocalName, attr.Value);
            }
        }

        xelem.ReplaceAttributes(attrs);
    }

    foreach (var elem in xelem.Elements())
        elem.FixDefaultXmlNamespace(documentDefaultNamespace);
}

请注意,如果在添加和检索属性时使用了前两种方法,则无需应用上述方法。