2009-02-11 6 views
62

sua una vbproj e assomiglia a questoXPath selezionare nodo con namespace

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 
    <PropertyGroup> 
     <ProjectGuid>15a7ee82-9020-4fda-a7fb-85a61664692d</ProjectGuid> 

tutto quello che voglio ottenere è la ProjectGuid ma non funziona quando un namespace è lì ...

Dim xmlDoc As New XmlDocument() 
Dim filePath As String = Path.Combine(mDirectory, name + "\" + name + ".vbproj") 
xmlDoc.Load(filePath) 
Dim value As Object = xmlDoc.SelectNodes("/Project/PropertyGroup/ProjectGuid") 

Cosa posso fare per risolvere questo problema?

+1

Due problemi con la soluzione di annakata: 1. E 'brutto, 2. In questo caso può essere utilizzata ma fornirà risultati sbagliati se un elemento 'ProjectGuid' appartiene a più di un namespace e vogliamo gli elementi solo da un singolo spazio dei nomi. Le soluzioni che utilizzano NamespaceManager sono migliori –

+0

Il motore XPath deve disporre del contesto statico corretto contenente i collegamenti tra prefissi e URI NS da utilizzare durante la valutazione di espressioni o non sarà possibile fare riferimento a roba all'interno di spazi dei nomi. Questo è ciò che fa @Teun. – lkuty

risposta

43

Il modo migliore per fare cose come questa (IMHO) è creare un gestore di namespace. Questo può essere usato chiamando SelectNodes per indicare quali URL dello spazio dei nomi sono collegati a quali prefissi. Io di solito ha installato una proprietà statica che restituisce un'istanza adeguata come questo (è C#, si dovrà tradurre):

private static XmlNamespaceManager _nsMgr; 
public static XmlNamespaceManager NsMgr 
{ 
    get 
    { 
    if (_nsMgr == null) 
    { 
     _nsMgr = new XmlNamespaceManager(new NameTable()); 
     _nsMgr.AddNamespace("msb", "http://schemas.microsoft.com/developer/msbuild/2003"); 
    } 
    return _nsMgr; 
    } 
} 

ho includono un solo spazio dei nomi qui, ma si potrebbe avere di più. Quindi è possibile selezionare dal documento come questo:

Dim value As Object = xmlDoc.SelectNodes("/msb:Project/msb:PropertyGroup/msb:ProjectGuid", NsMgr) 

Si noti che tutti gli elementi sono nello spazio dei nomi specificato.

+1

non è necessario creare un nuovo XmlDocument per ottenere un XmlNameTable. puoi usare nsMgr = new XmlNamespaceManager (new NameTable()); – foson

+0

Ah, grazie. Non ho mai scoperto come farlo. Il nuovo NameTable() era già possibile in .NET 1.0? –

+2

È incredibile quanto tempo può risparmiare a lungo termine per utilizzare correttamente gli spazi dei nomi. –

-7

Perché non utilizzare il // di ignorare lo spazio dei nomi:

Dim value As Object = xmlDoc.SelectNodes("//ProjectGuid") 

// agisce come wild card a seguire attraverso tutto tra la radice e il nome successivo nodo specificato (cioè ProjectGuid)

+7

in realtà non funziona - sì, questo significa cercare eventuali ProjectGuids ovunque, ma li vuole ancora nello spazio dei nomi predefinito – annakata

63

I 'd probabilmente essere tentati di andare con * soluzione di spazio dei nomi di Bartek, ma una soluzione generale XPath è:

//*[local-name()='ProjectGuid']

** dato che la risposta di Bartek è scomparsa, raccomando Teun (che in realtà è più approfondito) *

+0

concordato, anche se questo diventa un vero PITA quando devi andare più di un paio di livelli in profondità. Funziona, però. :) – ZombieSheep

+0

abbastanza, ecco perché vorrei andare con Barteks - l'unica cosa che mi ferma è se non conosco lo spazio dei nomi in anticipo o non posso garantirlo, nel qual caso probabilmente laverei prima l'intero documento , ma dicendo così otterrete solo i downvotes dello stalker :) – annakata

+0

Due problemi con questo: 1. È brutto, 2. In questo caso può essere usato ma fornirà risultati errati se un elemento 'ProjectGuid' appartiene a più di un namespace e vogliamo gli elementi solo da un singolo spazio dei nomi. Le soluzioni che utilizzano NamespaceManager sono migliori. –

27

Questo problema è stato qui severaltimesalready.

O si lavora con espressioni XPath namespace-agnostico (non raccomandato per la sua goffaggine e il potenziale per le partite falsi positivi - <msb:ProjectGuid> e <foo:ProjectGuid> sono gli stessi per questa espressione):

//*[local-name() = 'ProjectGuid']

o si fa la destra cosa e utilizzare un XmlNamespaceManager per registrare lo spazio dei nomi URI in modo da poter includere un prefisso dello spazio dei nomi nella vostra XPath:

Dim xmlDoc As New XmlDocument() 
xmlDoc.Load(Path.Combine(mDirectory, name, name + ".vbproj")) 

Dim nsmgr As New XmlNamespaceManager(xmlDoc.NameTable) 
nsmgr.AddNamespace("msb", "http://schemas.microsoft.com/developer/msbuild/2003") 

Dim xpath As String = "/msb:Project/msb:PropertyGroup/msb:ProjectGuid" 
Dim value As Object = xmlDoc.SelectNodes(xpath, nsmgr) 
3

È necessario solo per registrare questo namespace XML e Associ mangiato con un prefisso, per far funzionare la query. Creare e passare un gestore di spazio dei nomi, come secondo parametro nella scelta dei nodi:

Dim ns As New XmlNamespaceManager (xmlDoc.NameTable) 
ns.AddNamespace ("msbuild", "http://schemas.microsoft.com/developer/msbuild/2003") 
Dim value As Object = xmlDoc.SelectNodes("/msbuild:Project/msbuild:PropertyGroup/msbuild:ProjectGuid", ns) 
0

Un modo è quello di utilizzare le estensioni + NameSpaceManager.
Il codice è in VB ma è davvero facile da tradurre in C#.

Imports System.Xml 
Imports System.Runtime.CompilerServices 

Public Module Extensions_XmlHelper 

    'XmlDocument Extension for SelectSingleNode 
    <Extension()> 
    Public Function _SelectSingleNode(ByVal XmlDoc As XmlDocument, xpath As String) As XmlNode 
     If XmlDoc Is Nothing Then Return Nothing 

     Dim nsMgr As XmlNamespaceManager = GetDefaultXmlNamespaceManager(XmlDoc, "x") 
     Return XmlDoc.SelectSingleNode(GetNewXPath(xpath, "x"), nsMgr) 
    End Function 

    'XmlDocument Extension for SelectNodes 
    <Extension()> 
    Public Function _SelectNodes(ByVal XmlDoc As XmlDocument, xpath As String) As XmlNodeList 
     If XmlDoc Is Nothing Then Return Nothing 

     Dim nsMgr As XmlNamespaceManager = GetDefaultXmlNamespaceManager(XmlDoc, "x") 
     Return XmlDoc.SelectNodes(GetNewXPath(xpath, "x"), nsMgr) 
    End Function 


    Private Function GetDefaultXmlNamespaceManager(ByVal XmlDoc As XmlDocument, DefaultNamespacePrefix As String) As XmlNamespaceManager 
     Dim nsMgr As New XmlNamespaceManager(XmlDoc.NameTable) 
     nsMgr.AddNamespace(DefaultNamespacePrefix, XmlDoc.DocumentElement.NamespaceURI) 
     Return nsMgr 
    End Function 

    Private Function GetNewXPath(xpath As String, DefaultNamespacePrefix As String) As String 
     'Methode 1: The easy way 
     Return xpath.Replace("/", "/" + DefaultNamespacePrefix + ":") 

     ''Methode 2: Does not change the nodes with existing namespace prefix 
     'Dim Nodes() As String = xpath.Split("/"c) 
     'For i As Integer = 0 To Nodes.Length - 1 
     ' 'If xpath starts with "/", don't add DefaultNamespacePrefix to the first empty node (before "/") 
     ' If String.IsNullOrEmpty(Nodes(i)) Then Continue For 
     ' 'Ignore existing namespaces prefixes 
     ' If Nodes(i).Contains(":"c) Then Continue For 
     ' 'Add DefaultNamespacePrefix 
     ' Nodes(i) = DefaultNamespacePrefix + ":" + Nodes(i) 
     'Next 
     ''Create and return then new xpath 
     'Return String.Join("/", Nodes) 
    End Function 

End Module 

E per usarlo:

Imports Extensions_XmlHelper 

...... 
Dim FileXMLTextReader As New XmlTextReader(".....") 
FileXMLTextReader.WhitespaceHandling = WhitespaceHandling.None 
Dim xmlDoc As XmlDocument = xmlDoc.Load(FileXMLTextReader) 
FileXMLTextReader.Close() 
...... 
Dim MyNode As XmlNode = xmlDoc._SelectSingleNode("/Document/FirstLevelNode/SecondLevelNode") 

Dim MyNode As XmlNodeList = xmlDoc._SelectNodes("/Document/FirstLevelNode/SecondLevelNode") 

......