2009-02-11 34 views
62

Its a .vbproj và trông như thế nàyXPath chọn nút với namespace

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

tất cả tôi muốn nhận được các ProjectGuid nhưng nó không hoạt động khi một namespace là có ...

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") 

Tôi có thể làm gì để sửa lỗi này?

+1

Hai vấn đề với giải pháp annakata của: 1. Nó là xấu xí, 2. Trong trường hợp này nó có thể được sử dụng nhưng sẽ cung cấp kết quả sai nếu yếu tố một 'ProjectGuid' thuộc về nhiều hơn một không gian tên và chúng tôi chỉ muốn các phần tử từ một không gian tên duy nhất. Các giải pháp sử dụng NamespaceManager là tốt hơn –

+0

Công cụ XPath phải được cung cấp đúng ngữ cảnh có chứa các ràng buộc giữa các tiền tố và NS URI để sử dụng khi đánh giá các biểu thức hoặc bạn sẽ không thể tham chiếu nội dung bên trong không gian tên. Đây là những gì @Teun làm. – lkuty

Trả lời

43

Cách tốt nhất để làm những việc như thế này (IMHO) là tạo trình quản lý không gian tên. Điều này có thể được sử dụng để gọi SelectNodes để cho biết URL không gian tên nào được kết nối với các tiền tố nào. Tôi thường thiết lập một thuộc tính tĩnh mà trả về một thể hiện đầy đủ như thế này (C nó #, bạn sẽ phải dịch):

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; 
    } 
} 

tôi chỉ bao gồm một không gian tên ở đây, nhưng bạn có thể có nhiều. Sau đó, bạn có thể chọn từ tài liệu như sau:

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

Lưu ý rằng tất cả các phần tử đều nằm trong không gian tên được chỉ định.

+1

bạn không cần phải tạo một XmlDocument mới để có được một XmlNameTable. bạn có thể sử dụng nsMgr = new XmlNamespaceManager (new NameTable()); – foson

+0

Ah, cảm ơn. Tôi chưa bao giờ tìm ra cách để làm điều đó. Tên mới() đã có trong .NET 1.0 chưa? –

+2

Thật đáng ngạc nhiên là có bao nhiêu thời gian nó có thể tiết kiệm trong thời gian dài để sử dụng không gian tên một cách chính xác ngay từ đầu. –

-7

Tại sao không sử dụng // để bỏ qua không gian tên:

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

// hoạt động như thẻ hoang dã để làm theo thông qua tất cả mọi thứ giữa gốc và tên nút tiếp theo quy định (ví dụ ProjectGuid)

+7

không thực sự hoạt động - có điều này nói hãy tìm bất kỳ ProjectGuids nào, nhưng nó vẫn muốn chúng trong không gian tên mặc định – annakata

63

tôi 'lẽ muốn được nghiêng đi với giải pháp namespace * Bartek, nhưng một giải pháp xpath chung là:

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

** vì câu trả lời của Bartek đã biến mất, tôi khuyên Teun (thực sự là kỹ lưỡng hơn) *

+0

Đồng ý, mặc dù điều này trở thành PITA thực khi bạn phải đi sâu hơn một vài cấp. Nó * không * làm việc, mặc dù. :) – ZombieSheep

+0

khá, đó là lý do tại sao tôi đi với Barteks - điều duy nhất ngăn cản tôi ở đó là nếu tôi không biết không gian tên trước hoặc không thể đảm bảo không gian tên, trong trường hợp đó tôi có thể rửa toàn bộ tài liệu trước , nhưng nói như vậy sẽ chỉ làm cho tôi bị downvotes stalker :) – annakata

+0

Hai vấn đề với điều này: 1. Nó là xấu xí, 2. Trong trường hợp này nó có thể được sử dụng nhưng sẽ cung cấp kết quả sai nếu một yếu tố 'ProjectGuid' thuộc về nhiều hơn một không gian tên và chúng tôi chỉ muốn các phần tử từ một không gian tên duy nhất. Các giải pháp sử dụng NamespaceManager tốt hơn. –

27

Sự cố này đã ở đây severaltimesalready.

Dù bạn làm việc với các biểu thức XPath namespace-agnostic (không khuyến khích cho sự vụng về của mình và tiềm năng cho các trận đấu dương tính giả - <msb:ProjectGuid><foo:ProjectGuid> là như nhau cho biểu thức này):

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

hoặc bạn làm đúng các điều và sử dụng một XmlNamespaceManager để đăng ký namespace URI để bạn có thể bao gồm một tiền tố namespace trong XPath của bạn:

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

bạn chỉ cần đăng ký này không gian tên XML và associ ăn với một tiền tố, để làm cho truy vấn hoạt động. Tạo và vượt qua một người quản lý không gian tên như tham số thứ hai khi lựa chọn các nút:

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

Một cách là sử dụng phần mở rộng + NameSpaceManager.
Mã bằng VB nhưng thực sự dễ dàng dịch sang 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 

Và để sử dụng nó:

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") 

...... 
Các vấn đề liên quan