Lendo informações tag XML em VB.NET
Pergunta
Uma possível (de trabalho) Solução:
Private Sub ReadXMLAttributes(ByVal oXML As String)
ReadXMLAttributes(oXML, "mso-infoPathSolution")
End Sub
Private Sub ReadXMLAttributes(ByVal oXML As String, ByVal oTagName As String)
Try
Dim XmlDoc As New Xml.XmlDocument
XmlDoc.LoadXml(oXML)
oFileInfo = New InfoPathDocument
Dim XmlNodes As Xml.XmlNodeList = XmlDoc.GetElementsByTagName(oTagName)
For Each xNode As Xml.XmlNode In XmlNodes
With xNode
oFileInfo.SolutionVersion = .Attributes(InfoPathSolution.solutionVersion).Value
oFileInfo.ProductVersion = .Attributes(InfoPathSolution.productVersion).Value
oFileInfo.PIVersion = .Attributes(InfoPathSolution.PIVersion).Value
oFileInfo.href = .Attributes(InfoPathSolution.href).Value
oFileInfo.name = .Attributes(InfoPathSolution.name).Value
End With
Next
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.OkOnly, "ReadXMLAttributes")
End Try
End Sub
Isso funciona, mas vai ainda sofrem com o problema abaixo se os atributos são reordenados. A única maneira que eu posso pensar para evitar este problema é codificar os nomes de atributos em meu programa, e tê-lo processar a entrada por looping através da tag analisado e procurando as tags designadas.
NOTA: InfoPathDocument é uma classe personalizada que fiz, não é nada complicado:
Public Class InfoPathDocument
Private _sVersion As String
Private _pVersion As String
Private _piVersion As String
Private _href As String
Private _name As String
Public Property SolutionVersion() As String
Get
Return _sVersion
End Get
Set(ByVal value As String)
_sVersion = value
End Set
End Property
Public Property ProductVersion() As String
Get
Return _pVersion
End Get
Set(ByVal value As String)
_pVersion = value
End Set
End Property
Public Property PIVersion() As String
Get
Return _piVersion
End Get
Set(ByVal value As String)
_piVersion = value
End Set
End Property
Public Property href() As String
Get
Return _href
End Get
Set(ByVal value As String)
If value.ToLower.StartsWith("file:///") Then
value = value.Substring(8)
End If
_href = Form1.PathToUNC(URLDecode(value))
End Set
End Property
Public Property name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
Sub New()
End Sub
Sub New(ByVal oSolutionVersion As String, ByVal oProductVersion As String, ByVal oPIVersion As String, ByVal oHref As String, ByVal oName As String)
SolutionVersion = oSolutionVersion
ProductVersion = oProductVersion
PIVersion = oPIVersion
href = oHref
name = oName
End Sub
Public Function URLDecode(ByVal StringToDecode As String) As String
Dim TempAns As String = String.Empty
Dim CurChr As Integer = 1
Dim oRet As String = String.Empty
Try
Do Until CurChr - 1 = Len(StringToDecode)
Select Case Mid(StringToDecode, CurChr, 1)
Case "+"
oRet &= " "
Case "%"
oRet &= Chr(Val("&h" & Mid(StringToDecode, CurChr + 1, 2)))
CurChr = CurChr + 2
Case Else
oRet &= Mid(StringToDecode, CurChr, 1)
End Select
CurChr += 1
Loop
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.OkOnly, "URLDecode")
End Try
Return oRet
End Function
End Class
Original Pergunta ??h1>
Eu estou trabalhando em um projeto que requer a leitura de um documento XML, particularmente um formulário salvo da Microsoft InfoPath.
Aqui está um exemplo simples do que vou estar trabalhando com, juntamente com algumas informações básicas que podem ser úteis:
<?xml version="1.0" encoding="UTF-8"?>
<?mso-infoPathSolution solutionVersion="1.0.0.2" productVersion="12.0.0" PIVersion="1.0.0.0" href="file:///C:\Users\darren\Desktop\simple_form.xsn" name="urn:schemas-microsoft-com:office:infopath:simple-form:-myXSD-2009-05-15T14-16-37" ?>
<?mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.2"?>
<my:myFields xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2009-05-15T14:16:37" xml:lang="en-us">
<my:first_name>John</my:first_name>
<my:last_name>Doe</my:last_name>
</my:myFields>
Meu objetivo agora é extrair o versionID e local do formulário. bastante fácil com regex:
Dim _doc As New XmlDocument
_doc.Load(_thefile)
Dim oRegex As String = "^solutionVersion=""(?<sVersion>[0-9.]*)"" productVersion=""(?<pVersion>[0-9.]*)"" PIVersion=""(?<piVersion>[0-9.]*)"" href=""(?<href>.*)"" name=""(?<name>.*)""$"
Dim rx As New Regex(oRegex), m As Match = Nothing
For Each section As XmlNode In _doc.ChildNodes
m = rx.Match(section.InnerText.Trim)
If m.Success Then
Dim temp As String = m.Groups("name").Value.Substring(m.Groups("name").Value.ToLower.IndexOf("infopath") + ("infopath").Length + 1)
fileName = temp.Substring(0, temp.LastIndexOf(":"))
fileVersion = m.Groups("sVersion").Value
End If
Next
O único problema que esta solução de trabalho traz à tona é se o esquema muda no cabeçalho do documento InfoPath ... por exemplo as propriedades versão solução e versão do produto trocar locais (AMORES Microsoft está fazendo coisas como esta, parece).
Então, eu optou por tentar usar o XML analisar a capacidade de VB.NET para me ajudar a alcançar os resultados acima, sans-regex.
O ChildNode
do objeto _doc
que contém a necessidade de informação I, no entanto, ele não tem nenhum ChildNodes:
_doc.ChildNode(1).HasChildNodes = False
Alguém pode me ajudar com isso?
Solução
As instruções de processamento são parte do documento XML, mas seus atributos não se analisado. Tente este código:
// Load the original xml...
var xml = new XmlDocument();
xml.Load( _thefile );
// Select out the processing instruction...
var infopathProcessingInstruction = xml.SelectSingleNode( "/processing-instruction()[local-name(.) = \"mso-infoPathSolution\"]" );
// Since the processing instruction does not expose it's attributes, create a new XML document...
var xmlInfoPath = new XmlDocument();
xmlInfoPath.LoadXml("<data " + infopathProcessingInstruction.InnerText + " />");
// Get the data...
var solutionVersion = xmlInfoPath.DocumentElement.GetAttribute("solutionVersion");
var productVersion = xmlInfoPath.DocumentElement.GetAttribute("productVersion");
Outras dicas
O problema é que as tags que você deseja parse não são realmente parte do documento XML. Eles são o XML-Prolog que contém as instruções de processamento. E assim eles não estarão disponíveis no XmlDocument como elementos.
Minha única idéia seria (para além de olhar através da documentação como se poderia aceder a estes elementos) para mover apenas o mso-infoPathSolution elemento em um XmlDocument própria, após a remoção do ?> Afastado e substitui-los com >. Então você pode acessar os atributos independentemente da sua ordenação.