mac*_*ojw 61 c# xml xpath namespaces xpathnavigator
我有一个带有默认命名空间的XML文档.我正在使用XPathNavigator使用Xpath选择一组节点,如下所示:
XmlElement myXML = ...;
XPathNavigator navigator = myXML.CreateNavigator();
XPathNodeIterator result = navigator.Select("/outerelement/innerelement");
Run Code Online (Sandbox Code Playgroud)
我没有得到任何结果:我假设这是因为我没有指定命名空间.如何在我的选择中包含命名空间?
Mar*_*ell 80
首先 - 你不需要导航仪; SelectNodes/SelectSingleNode应该足够了.
但是,您可能需要命名空间管理器 - 例如:
XmlElement el = ...; //TODO
XmlNamespaceManager nsmgr = new XmlNamespaceManager(
el.OwnerDocument.NameTable);
nsmgr.AddNamespace("x", el.OwnerDocument.DocumentElement.NamespaceURI);
var nodes = el.SelectNodes(@"/x:outerelement/x:innerelement", nsmgr);
Run Code Online (Sandbox Code Playgroud)
Che*_*eso 48
您可能想尝试使用XPath Visualizer工具来帮助您完成.
XPathVisualizer是免费的,易于使用.
重要说明:如果您使用的是Windows 7/8但未看到"文件","编辑"和"帮助菜单"项,请按ALT键.
Mit*_*lik 26
对于那些寻找快速入侵解决方案的人来说,特别是在你知道 XML并且不需要担心命名空间的情况下,你可以通过简单地将文件读取到一个字符串来解决这个烦人的小"功能".取代令人反感的属性:
XmlDocument doc = new XmlDocument();
string fileData = File.ReadAllText(fileName);
fileData = fileData.Replace(" xmlns=\"", " whocares=\"");
using (StringReader sr = new StringReader(fileData))
{
doc.Load(sr);
}
XmlNodeList nodeList = doc.SelectNodes("project/property");
Run Code Online (Sandbox Code Playgroud)
我发现这比在处理单个文件时需要默认命名空间的前缀的所有其他非意义更容易.希望这可以帮助.
Tom*_*icz 20
在具有命名空间的XML上使用.NET中的XPath(通过导航器或SelectNodes/SelectSingleNode)时,您需要:
提供您自己的XmlNamespaceManager
并显式地在XPath表达式中添加所有元素,这些元素位于命名空间中.
后者是(从下面链接的MS源转述):因为XPath 1.0忽略默认命名空间规范(xmlns ="some_namespace").因此,当您使用不带前缀的元素名称时,它假定为null namespace.
这就是为什么.NET实现XPath忽略了XmlNamespaceManager中前缀为String.Empty的命名空间,并且总是使用null命名空间.
有关详细信息,请参阅XmlNamespaceManager和UndefinedXsltContext不处理默认命名空间.
我发现这个"功能"非常不方便,因为你不能通过简单地添加默认命名空间声明来使旧的XPath命名空间感知,但这就是它的工作原理.
小智 6
您可以在不使用XmlNamespaceManager的情况下使用XPath语句,如下所示:
...
navigator.Select("//*[ local-name() = 'innerelement' and namespace-uri() = '' ]")
...
Run Code Online (Sandbox Code Playgroud)
这是在XML中选择默认命名空间的元素的简单方法.
重点是使用:
namespace-uri() = ''
Run Code Online (Sandbox Code Playgroud)
这将找到具有默认命名空间的元素而不使用前缀.
我的回答扩展了布兰登之前的回答.我用他的例子创建了一个扩展方法,如下所示:
static public class XmlDocumentExt
{
static public XmlNamespaceManager GetPopulatedNamespaceMgr(this System.Xml.XmlDocument xd)
{
XmlNamespaceManager nmsp = new XmlNamespaceManager(xd.NameTable);
XPathNavigator nav = xd.DocumentElement.CreateNavigator();
foreach (KeyValuePair<string,string> kvp in nav.GetNamespacesInScope(XmlNamespaceScope.All))
{
string sKey = kvp.Key;
if (sKey == "")
{
sKey = "default";
}
nmsp.AddNamespace(sKey, kvp.Value);
}
return nmsp;
}
}
Run Code Online (Sandbox Code Playgroud)
然后在我的XML解析代码中,我只添加一行:
XmlDocument xdCandidate = new XmlDocument();
xdCandidate.Load(sCandidateFile);
XmlNamespaceManager nmsp = xdCandidate.GetPopulatedNamespaceMgr(); // 1-line addition
XmlElement xeScoreData = (XmlElement)xdCandidate.SelectSingleNode("default:ScoreData", nmsp);
Run Code Online (Sandbox Code Playgroud)
我非常喜欢这种方法,因为它在从源XML文件加载命名空间方面是完全动态的,并且它并没有完全忽略XML命名空间的概念,因此这可以与需要多个命名空间的XML一起用于解除冲突.
如果名称空间因outerelement和innerelement而异
XmlNamespaceManager manager = new XmlNamespaceManager(myXmlDocument.NameTable);
manager.AddNamespace("o", "namespaceforOuterElement");
manager.AddNamespace("i", "namespaceforInnerElement");
string xpath = @"/o:outerelement/i:innerelement"
// For single node value selection
XPathExpression xPathExpression = navigator.Compile(xpath );
string reportID = myXmlDocument.SelectSingleNode(xPathExpression.Expression, manager).InnerText;
// For multiple node selection
XmlNodeList myNodeList= myXmlDocument.SelectNodes(xpath, manager);
Run Code Online (Sandbox Code Playgroud)
我遇到了类似的空白默认命名空间问题.在这个示例XML中,我有一些带有名称空间前缀的元素和一个没有的单个元素(DataBlock):
<src:SRCExample xmlns="urn:some:stuff:here" xmlns:src="www.test.com/src" xmlns:a="www.test.com/a" xmlns:b="www.test.com/b">
<DataBlock>
<a:DocID>
<a:IdID>7</a:IdID>
</a:DocID>
<b:Supplimental>
<b:Data1>Value</b:Data1>
<b:Data2/>
<b:Extra1>
<b:More1>Value</b:More1>
</b:Extra1>
</b:Supplimental>
</DataBlock>
</src:SRCExample>
Run Code Online (Sandbox Code Playgroud)
我试图使用在XPath Visualizer中工作的XPath,但在我的代码中不起作用:
XmlDocument doc = new XmlDocument();
doc.Load( textBox1.Text );
XPathNavigator nav = doc.DocumentElement.CreateNavigator();
XmlNamespaceManager nsman = new XmlNamespaceManager( nav.NameTable );
foreach ( KeyValuePair<string, string> nskvp in nav.GetNamespacesInScope( XmlNamespaceScope.All ) ) {
nsman.AddNamespace( nskvp.Key, nskvp.Value );
}
XPathNodeIterator nodes;
XPathExpression failingexpr = XPathExpression.Compile( "/src:SRCExample/DataBlock/a:DocID/a:IdID" );
failingexpr.SetContext( nsman );
nodes = nav.Select( failingexpr );
while ( nodes.MoveNext() ) {
string testvalue = nodes.Current.Value;
}
Run Code Online (Sandbox Code Playgroud)
我将其缩小到XPath的"DataBlock"元素,但除了通配简单的DataBlock元素之外无法使其工作:
XPathExpression workingexpr = XPathExpression.Compile( "/src:SRCExample/*/a:DocID/a:IdID" );
failingexpr.SetContext( nsman );
nodes = nav.Select( failingexpr );
while ( nodes.MoveNext() ) {
string testvalue = nodes.Current.Value;
}
Run Code Online (Sandbox Code Playgroud)
经过大量的头部搜索和谷歌搜索(让我来到这里)后,我决定直接在我的XmlNamespaceManager加载器中处理默认命名空间,方法是将其更改为:
foreach ( KeyValuePair<string, string> nskvp in nav.GetNamespacesInScope( XmlNamespaceScope.All ) ) {
nsman.AddNamespace( nskvp.Key, nskvp.Value );
if ( nskvp.Key == "" ) {
nsman.AddNamespace( "default", nskvp.Value );
}
}
Run Code Online (Sandbox Code Playgroud)
所以现在"默认"和""指向相同的命名空间.一旦我这样做,XPath"/ src:SRCExample/default:DataBlock/a:DocID/a:IdID"就像我想要的那样返回了我的结果.希望这有助于澄清其他人的问题.