如何为XDocument设置默认的XML名称空间
我怎样才能设置现有的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.
有任何想法吗?
Linq to XML似乎没有为这个用例提供一个API(声明:我没有深入调查)。 如果更改根元素的名称空间,如下所示:
XNamespace xmlns = "http://schemas.datacontract.org/2004/07/Widgets"; doc.Root.Name = xmlns + doc.Root.Name.LocalName;
只有根元素的名字空间才会改变。 所有的孩子都会有一个空的xmlns标签。
解决scheme可能是这样的:
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)); }
不知道如果这已经在.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"
语法。
我有同样的要求,但是我想出了一些不同的东西:
/// <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); } }
如果你想正确地做,你必须考虑,你的元素可能包含不同名称空间的扩展元素。 您不希望全部更改它们,但只需要那些默认的命名空间元素。
R. Martinho Fernandes上面的答案(不改变现有的文档)只需要一个小的调整,以便元素值也被返回。 我没有testing过这种焦虑,只是在玩linqpad,抱歉没有提供unit testing。
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)); }
修改扩展方法以包含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)); }
现在它适用于我!
不要忘记还要复制其余的属性:
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; }