Recupero dati da un foglio Excel 2007 col VBA, seconda soluzione
Questo articolo fa seguito al mio primo intervento in materia:
(è importante leggerlo, prima di proseguire…)
Prima di procedere, offro agli amanti delle varianti, una prima alternativa alla precedente soluzione.
Questa può essere resa più efficiente sostituendo il filtro della soluzione originale, che ottiene tutti i valori, senza distinguere quelli che sono indici di shared string, ossia, ripeto:
Set NodiXmlRif = DocXml.selectNodes("//sheetData/row/c/@r")
Set NodiXmlVal = DocXml.selectNodes("//sheetData/row/c/v")
con il filtro seguente, che seleziona I soli valori “reali” (numerici), riducendo così i tempi del caricamento di dati nelle celle del foglio ricostruito:
Set NodiXmlRif = DocXml.selectNodes("//sheetData/row/c[@s]/@r")
Set NodiXmlVal = DocXml.selectNodes("//sheetData/row/c[@s]/v")
Nota. La soluzione seguente di primo acchito può apparire valida:
Set NodiXmlRif = DocXml.selectNodes("//sheetData/row/c [@t!='s']/@r")
Set NodiXmlVal = DocXml.selectNodes("//sheetData/row/c[@t!='s']/v")
ma di fatto restituisce zero nodi, in quanto le celle contenenti valori “veri” non contengono nessun attributo t, bensì solo l’attributo s (esempio s=”3”).
(l’XPath è potente ma assai sottile,... idem l’OOXML!)
La nuova soluzione
Nel primo articolo avevo sinceramente confessato di non aver ben compreso come si applicasse la proprietà Attributes di un nodo XML (oggetto IXMLDOMNode), facendo appello a chi lo sapesse. La richiesta è caduta nel vuoto (suscitando commiserazione da parte degli esperti in materia? Non credo, oggi come oggi sono ben pochi...), Comunque nel frattempo ho compreso che il mio fraintendimento era, semplicemente, infondato (dico solo che dovevo far uso della proprietà parentNode, v. più avanti)
Sia come sia, ho così sviluppato un’alternativa basata sulla dannata proprietà Attributes In due parole, il nuovo algoritmo evita del tutto il filtraggio preventivo dei nodi c, li esplora tutti, utilizzando ciascuno di essi in conformità ai suoi attributi. Altra variante interessante consiste nell’uso diretto di sharedStrings.xml, in luogo del vettore della precedente soluzione.
Nota bene. sharedStrings.xml viene caricato in uno specifico DOMDocument, ossia DocXmlCond, distinto rispetto a DocXml, In tal modo essi si possono usare separatamente.
La nuova macro:
Sub RicostrDatiByAttrib(Cartxml As String)
Dim DocXml As DOMDocument, DocXmlCond As DOMDocument
Dim NodiXmlCond As IXMLDOMNodeList 'shared strings
Dim NodiXml As IXMLDOMNodeList 'Tutti i nodi
Dim NodoXml As IXMLDOMNode
Set DocXmlCond = New DOMDocument 'DOMDocument per le sharedStrings
DocXmlCond.async = False
DocXmlCond.Load ("" & Cartxml & "\xl\sharedStrings.xml")
Set NodiXmlCond = DocXmlCond.selectNodes("//t")
'Esplora tutte le celle
Set DocXml = New DOMDocument 'NB – Occorre un diverso DOMDocument
DocXml.async = False
DocXml.Load ("" & Cartxml & "\xl\worksheets\sheet1.xml")
Set NodiXml = DocXml.selectNodes("//c/v")
Dim NodoGenitore As IXMLDOMNode
Dim Rifer As String, ValNodo As String, MaxAttr As Integer
For Each NodoXml In NodiXml
Set NodoGenitore = NodoXml.parentNode
ValNodo = NodoXml.Text
Rifer = NodoGenitore.Attributes(0).Text
MaxAttr = NodoGenitore.Attributes.Length - 1
If NodoGenitore.Attributes(MaxAttr).Text = "s" Then
With Range(Rifer)
.Value = NodiXmlCond(ValNodo).Text
With .Interior
.ThemeColor = xlThemeColorAccent5
.TintAndShade = 0.6
End With
End With
Else
Range(Rifer) = ValNodo
End If
Next
End Sub
'Un semplice test:
Sub ProvaRicostrDati ()
With WorkSheets(1)
.Activate
.UsedRange.Clear
End With
RicostrDatiByAttrib "C:\CartMioFoglio"
End Sub
Anche qui l’ipotesi è che nella cartella di file C:\CartMioFoglio siano stati estratti gli archivi xml del nostro MioFoglio.xlsx (v. articolo precedente).
Altre osservazioni
· NodiXmlRif e NodiXmlVal fanno riferimento ai nodi v (valori) e @r (riferimenti di cella) come nella soluzione precedente, ma senza filtraggio.
· Gli attributi che ci interessano sono quelli dei nodi c¸ i quali si possono ottenere con l’istruzione ripetuta qui sotto per comodità:
Set NodoGenitore = NodoXml.parentNode
· Gli attributi del nodo ‘c possono essere 2 o 3, comunque quello che ci interessa è l’ultimo, pertanto l’istruzione seguente fornisce il suo indice nell’insieme Attributes (N.B. l’indice del primo elemento s è 0):
MaxAttr = NodoGenitore.Attributes.Length – 1
?>