考虑生成以下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>
请注意root
和E1
中的重复名称空间定义以及p3
下E1
前缀为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}}
但是我不喜欢这种解决方案,主要是因为它很贵。我觉得我在某个地方错过了一个小小的场景。有什么想法吗?
答案 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);
}
请注意,如果在添加和检索属性时使用了前两种方法,则无需应用上述方法。