Gestione VBA del formato aperto OOXML di Excel
ULTIM’ORA: pensa e ripensa, ho trovato una variante alla routine di ricostruzione dati di un foglio di lavoro .XLSX che evita le (pur originali e... laboriose!) funzioni personali. Come sfruttando opportune sintassi XPath per compiere lo stesso mestiere.
Riporto la nuova versione in fondo a questo articolo. Istruttiva, direi, in altri casi analoghi.
Con Office 2007 Microsoft ha introdotto un nuovo, rivoluzionario formato: l’OOXML, Open Office XML format. Come è noto, i nuovi file Word, Excel e PowerPoint sono composti da un insieme di cartelle racchiuse in un file compresso (zippato) contenenti diversi componenti di testo puro (“parti” in gergo) per lo più in formato XML, contenenti tag, testi e attributi che ne descrivono la struttura, i dati, i formati e quant’altro. La rivoluzione risiede nel fatto che, a parte limitazioni – vere o presunte – su cui si è presto acceso un vivace dibattito, diventa possibile leggere, modificare e, in genere elaborare file .docx / .docm o .xlsx / .xlsm anche senza ricorrere a Word o Excel.
Cenno al “vivace dibattito”
Critiche scandalizzate, un po’ irose e, a onesto parere di chi scrive, in gran parte pretestuose sono subito piovute dai fan del formato aperto rivale, ovvero l’ODF (Open Document Format) che l’ISO (International Standard Organization) ha già approvato come standard. Microsoft invece ha in prima battuta avuto il bollo tondo dell’ECMA (European Computer Manufacturers Association) e ha aperto una procedura urgente con l’ISO per ottenere la ratifica anche del “suo”, ma ormai di pubblico dominio, OOXML. Contestualmente, ha emesso una solenne “promise” di non perseguire legalmente chiunque svilupperà applicativi, gratuiti aperti o commerciali, basati su OOXML.
Apriti cielo! I fautori dell’Open source (le cui iniziative di stimolo alla concorrenza sono in genere benemerite, per carità!) sono insorti, avanzando dubbi su tale promessa e facendo le pulci all’OOXML. Secondo loro, esso sarebbe pienamente gestibile solo con Office 2007.
Esagerati (a dir poco)! Il fatto è che, piaccia o non piaccia, lo standard de facto in questo mondo è dato da Microsoft Office, che domina nettamente il mercato. Dimenticavo: ODF, in pratica, vuol dire il pur grazioso rivale OpenOffice.org, alias StarOffice che è sì gratuito, ma prodotto quasi da un solo vendor, ossia Sun Microsystem, che vanta un market share di 10% circa (ci sarebbe anche l’IBM Lotus 1-2-3 nel campo Open Source e un altro Carneade: ma chi li ha visti?).
Sarò malignazzo, ma penso che le preoccupazioni dei supporter di questa gang of three siano evidenti: se ISO approva OOXML che fine fa l’ODF? Uno dei due standard è di troppo...
Chiudo questa parentesi con tre considerazioni:
- Un certo fondamento tecnico potrebbero avere le critiche al mancato supporto di un paio di standard ISO, come il MathML al cui posto OOXML adotta un formato proprietario; ma a parte che MathML è una “raccomandazione” non un obbligo, signori miei come si può pretendere di imporlo a migliaia di partner e a milioni di utenti MS Office? (comunque da ultimo pare che Microsoft si stia impegnando per ovviare alle più “costruttive” di queste critiche).
- Per contro, l’ODF (nato troppo in fretta?) a tutt’oggi non supporta le funzioni né le macro (beninteso, StarOffice le ha entrambe, ma “ufficialmente” ODF le ignora: vai a capire perché).
- Quanto ai sospetti sull’apertura reale di Bill Gates (la “promise” di cui sopra) basta ricordare che StarOffice da tempo emula (tramite reverse engineering) Office, macro VBA incluse!, senza che i legali della casa di Redmond abbiano fatto una piega. Ora lavorare su dei formati binari è impresa dura, mentre con OOXML diventa una passeggiata, come la mia semplice macro per normali power user dimostrerà.
Nota – Già nascono defezioni sul fronte dei vendor più o meno Open, tra cui Dell e Corel. La seconda ha già varato una versione OOXML del suo WordPerfect e magari Quattro Pro. E pure vari sviluppatori Java e Linux, con Sun in testa sono al lavoro in tale direzione. Costretta a farlo, dico io, se si vuole mantenere, anzi ampliare, la rispettiva quota di mercato.
Ricostruire col VBA un foglio Excel 2007
La ricetta che vado, audacemente, a proporre è una macro VBA che sfrutta i numerosi (*) comandi dedicati alla gestione dei file XML e di quelli OOXML in particolare.
Nota (*) – E, ahimè, non sempre ben documentati nell’Help (tranne che per chi conosce già la materia, tuttora ostica per molti).
Lo scopo è duplice:
a) Ricostruire i dati di un foglio di lavoro *.xlsx esterno (ossia posto sul disco), limitatamente ai valori e alle etichette, insomma uno spartano (ma veloce!) visore;
b) Dimostrare che tali operazioni, possibili per tutti, a tutti fanno toccare con mano quanto è davvero aperto il formato OOXML.
Qualcuno criticherà subito il ricorso a una macro VBA di Excel 2007. Obietto che la scelta, onestamente dovuta alla competenza primaria di chi scrive, ha il pregio di permettere a un vasto pubblico di fare questi esperimenti, che altri potrebbero svolgere con qualsiasi arnese software: VB . Net, C# e, magari, Java. A patto che sia dotato di analoghi mezzi come quelli, direi potenti e notevoli che ci passa il normale convento VBA, e che andremo in parte a sperimentare.
Prerequisiti:
- Nozioni base sul linguaggio XML;
- Idem per il sistema XPath di accesso alle varie foglie di un albero XML;
- Idem per la struttura dei file .xlsx (o xlsm).
Su questi punti mi manca del tutto lo spazio se non per sottintesi, posso solo invitare a fare ricerche su sito MSDN di Microsoft, nonché su http://openxmldeveloper.org. C’è un sacco di roba: leggetela!
Operazioni manuali preliminari
Per semplificare all’osso la faccenda si crei su un foglio 1 un elenco come il seguente:
|
|
A |
B |
C |
|
1 |
CITTA |
OPERAIO |
ORE |
|
2 |
Roma |
Antonio |
15,00 |
|
3 |
Firenze |
Mario |
12,00 |
|
4 |
Roma |
|
76,00 |
|
5 |
Roma |
|
79,00 |
|
6 |
Firenze |
Pio |
28,00 |
|
7 |
Bari |
Carlo |
16,00 |
|
8 |
Roma |
|
69,00 |
|
9 |
Bari |
|
16,00 |
|
10 |
Milano |
|
63,00 |
|
11 |
Bari |
|
56,00 |
|
12 |
Milano |
|
92,00 |
|
13 |
Milano |
|
65,00 |
|
14 |
|
|
|
Poi sulla destra dello stesso foglio si ponga una serie di tabelle come quelle qui sotto riportate, di cui non riporto le intestazioni di riga e colonna:
|
Città |
|
Roma |
Firenze |
Bari |
Milano |
|
Elenco |
|
Roma |
|
Luigi |
Ciccio |
Carlo |
Carlo |
|
Carlo |
|
Firenze |
|
Antonio |
Pio |
Carmine |
Mario |
|
Carmine |
|
Bari |
|
Giacomo |
Mario |
Ernesto |
|
|
Ernesto |
|
Milano |
|
Piero |
|
Mimmo |
|
|
Mimmo |
|
|
Enzo |
|
|
|
|
|
Salvare poi il file su C: come MioFoglio.xlsx.
Sia ben chiaro, è solo un esempio spicciolo, tanto per fissare il compito, ma potrà andar bene qualsiasi altro spreadsheet, ovunque collocato (localmente o, magari, sul Web).
A questo punto ricordo le mosse necessarie per visionare la struttura OOXML del nostro filetto (lezioso, deplorevole diminutivo di “file”, la carne pregiata qui non c’entra) salvandone le “parti” in una directory ad hoc:
- Preparare una cartella di file C:\CartMioFoglio;
- Rinominare MioFoglio.xlsx come MioFoglio.zip (modificando l’estensione da xlsx a zip) e aprire lo “zippo” risultante col doppio clic;
- Copiarne tutte le sottocartelle e relativi file in C:\CartMioFoglio e magari ridare al file .zip l’estensione .xlsx, riottenendo l’originale.
Nota – Queste operazioni, e quelle inverse, si possono svolgere anche con routine VBA (o VB .NET o quant’altro). Prima o poi pubblicherò quelle per VBA...
A questo punto esplorando C:\CartMioFoglio si potranno individuare i vari file XML e visualizzarli in Explorer. Per quel che ci occorre consideriamo solo i seguenti (la radice sottintesa è, ovviamente, la nostra C:\CartMioFoglio):
· Xl\worksheets\sheet1.xml
· Xl\sharedStrings.xml
Il primo descrive in “XML-lese” il foglio di lavoro 1, accanto ai fratelli sheet2.xlm, sheet3.xlm... Il secondo reca le stringhe condivise. Infatti OOXML racchiude in questo unico contenitore tutti i testi del workbook, che vengono referenziati tramite indice nei vari sheet1, sheet2 ecc. Qui consiglio di andare a visionare con Explorer o altro visore XML questi due file.
- Creare un file Excel 2007 a piacere, diciamo ProvaOOXML.xlsm (formato macro enabled);
- Passare all’Editor VBA (Alt+F11) e col comando Strumenti > Riferimenti… aggiungere Microsoft XML v6 (o v5) altrimenti i preziosi comandi per gestire l’XML non funzionano!;
- Non dimenticarsi, infine, di salvare da qualche parte ProvaOOXML.xlsm “curato” come nel passo precedente.
La macro fatidica: per esperti e con scarni commenti
Bando agli indugi e si copi in un qualche Modulo1 di ProvaOOXML.xlsm il codice VBA qui sotto riportato.
'FUNZIONI PRELIMINARI:
Function CellaCondivisa(CellaXml As String) As Boolean
'Indica se la cella fa riferimento a sharedStrings
Dim Pos2Ang As Integer
Pos2Ang = InStr(1, CellaXml, "><")
If Right(CellaXml, 2) = "/>" Then
CellaCondivisa = False
Exit Function
End If
If Mid(CellaXml, Pos2Ang - 5, 1) = "t" Then CellaCondivisa = True
End Function
Function RiferimCella(CellaXml As String)
Dim Pos0 As Integer 'Posizione di "http://" dopo "//"
Dim CellaXmlParz As String 'CellaXml dopo "http://"
Pos0 = InStr(1, CellaXml, "http://") + 6
CellaXmlParz = Right(CellaXml, Len(CellaXml) - Pos0)
Dim Pos1 As Integer 'Posizione della lettera del rifer. in CellaXmlParz.
Dim Pos2 As Integer 'Posizione dell'ultima cifra del rifer. in CellaXmlParz.
Pos1 = InStr(1, CellaXmlParz, "r=") + 3
Pos2 = InStr(Pos1, CellaXmlParz, " ") - 1
LungRif = Pos2 - Pos1
RiferimCella = Mid(CellaXmlParz, Pos1, Pos2 - Pos1)
End Function
Sub RicostrDati(CartXML As String)
'CartXML è la directory contenente le parti OOXML
'estratti da un file Excel 2007
Dim DocXml As DOMDocument
Dim NodiXml As IXMLDOMNodeList
Dim NodoXml As IXMLDOMNode
Set DocXml = New DOMDocument
DocXml.async = False 'inibisce la sincronizzazione DOM/origine
'permettendo accesso senza interferenze: MAI dimenticare!
DocXml.Load ("" & CartXML & "\xl\worksheets\sheet1.xml")
Set NodiXml = DocXml.selectNodes("//sheetData/row/c")
Dim i As Integer 'Indice per i vettori che seguono
Dim VettRifer() As String ‘Matrice dei riferimenti di cella
For Each NodoXml In NodiXml
ReDim Preserve VettRifer(i)
VettRifer(i) = RiferimCella(NodoXml.Xml)
‘MsgBox VettRifer(i) 'usata per debugging
i = i + 1
Next
Dim VettValori() As String
'Caricamento VettValori (inclusi indici delle sharedStrings!)
Set NodiXml = DocXml.selectNodes("//sheetData/row/c/v")
'N.B. Andrebbe bene pure "c/v" o persino "v"
i = 0 'Achtung! Si deve ricominciare da ZERO!!!
With Worksheets(3) 'Per semplicità, si usa il Foglio3; chi lo
'preferisce potrebbe creare un NUOVO foglio lavoro ad hoc
.Activate
.UsedRange.Delete
End With
For Each NodoXml In NodiXml
Range(VettRifer(i)) = NodoXml.Text
i = i + 1
Next
Dim VettCelleCondiv() As String
Dim VettValCelleCondiv() As Integer
'Caricamento Vettori delle celle condivise e dei rispettivi valori
Set NodiXml = DocXml.selectNodes("//sheetData/row/c")
Dim CellaXml As String
i = 0
For Each NodoXml In NodiXml
'Nota bene! Si vuole l'intera cella
CellaXml = NodoXml.Xml
If CellaCondivisa(CellaXml) Then
ReDim Preserve VettCelleCondiv(i)
ReDim Preserve VettValCelleCondiv(i)
VettCelleCondiv(i) = RiferimCella(CellaXml)
VettValCelleCondiv(i) = NodoXml.Text
i = i + 1
End If
Next
i = 0
Dim VettStrCondiv() As String
'Caricamento Vettore delle stringhe condivise
‘stavolta si deve reimpostare il documento caricato:
DocXml.Load ("" & DocumOOXML & "\xl\sharedStrings.xml")
Set NodiXml = DocXml.selectNodes("//t")
For Each NodoXml In NodiXml
ReDim Preserve VettStrCondiv(i)
'MsgBox NodoXml.Text ‘Servito per debugging
VettStrCondiv(i) = NodoXml.Text
i = i + 1
Next
'Il loop seguente è servitor per il debug:
'For i = 0 To UBound(VettCelleCondiv)
'Msg = Msg & VettCelleCondiv(i) & " - " & VettValCelleCondiv(i) & vbLf
'Next
'MsgBox Msg
For i = 0 To UBound(VettCelleCondiv)
With Range(VettCelleCondiv(i))
.Value = VettStrCondiv(VettValCelleCondiv(i))
.Font.Bold = True 'Neretto per evidenziare le
'stringhe condivise; ricostruire formati e stili
'è pure possible, ma esulava dai nostri scopi...
End With
Next
Set DocXml = Nothing
Set NodiXml = Nothing
Set NodoXml = Nothing
End Sub
'SUB DI PROVA DELLA ROUTINE PRECEDENTE
Sub ProvaRicostrDati()
RicostrDati "C:\CartMioFoglio"
End Sub
Lanciando ProvaRicostrDati si dovrebbero vedere sul Foglio3 i dati e le stringhe originari, prive di formati e di formule. Quanto basta per:
a) Ottenere l’essenziale, per successive elaborazioni; come ripeto e insisto, questi risultati oltre che con Excel si possono ottenere pure, che so?, con il Calc di StarOffice, oppure, mutatis mutandis, con una tabella Word, una pagina HTML, un applicativo VB .NET, C#, Java e chi più ne ha...
b) Dimostrare così che OOXML è sicuramente aperto, almeno per le cose che più interessano.
Nota – Ho esplorato nel formato OOXML il nuovo oggetto Table di Excel (ListObject già presente nella vers. 2003 e potenziato nella 2007. Ebbene la descrizione è chiarissima, per cui mi sento di affermare che anche questa emulazione è un gioco da ragazzi per sviluppatori professionali. E non mi si venga a dire che Bill Gates pretenderebbe royalty per un eventuale brevetto in materia (una volta che ISO approva OOXML, poi...
Tutto il succitato po’ po’ di codice, per squallida ignavia che confina (ma non “sconfina”) col sadismo, lo affido quasi soltanto all’esegesi autogestita dei più esperti. Ai quali mi limito ad alcune annotazioni essenziali.
Per cominciare riporto per sommi capi la struttura di uno sheet1.xml (per uno strano difetto dell’Editor di Shareoffice che s’impapocchia col carattere “<”; qui come altrove l’ho sostituito con cancelletto “#”):
#?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
#worksheet xmlns="http://schemas.openxmlformats.org/... >
#sheetPr codeName="Foglio1" />
#dimension ref="A1:O13" />
OMISSIS
#sheetData>
#row r="1" spans="1:15" ht="15.75" thickBot="1">
#c r="A1" s="3" t="s">
#v>0#/v>
#/c>
- #c r="B1" s="3" t="s">
#v>1#/v>
#/c>
- OMISSIS (altre tag #c> della prima riga)
#/row>
- #row r="2" spans="1:15" ht="15.75" thickTop="1">
OMISSIS (seconda riga)
#/row>
OMISSIS
#/sheetData>
#/worksheet>
Come è, perlomeno, intuitivo i dati del foglio, incastonati tra sheetData e /sheetData sono strutturati gerarchicamente secondo righe, racchiuse tra row e /row, a loro volta comprendenti elementi compresi tra c e /c, ovverosia celle, che infine racchiudono valori inseriti tra tag v e /v. Ma è sugli elementi c che nascono le complicazioni, in quanto contengono anche gli attributi r, s e t. Il significato del primo è chiaro: si tratta dei riferimenti della cella. Per gli altri due invito a consultare i testi sacri accennati sopra, dico solo che, tra l’altro, rimandano alle sharedStrings, nel qual caso v non va preso come valore, ma come l’indice dell’insieme contenuto in sharedStrings.xml.
A questo punto, i bravi & volonterosi cui mi rivolgo debbono andarsi a vedere l’appena citato sharedStrings.xml, constatandone la chiara presenza dei vari “CITTA”, “OPERAIO”, “ORE”, “Milano”, “Torino” e così via. Così se, per ipotesi, una certa cella ha un valore v=”3” che rimanda alle stringhe condivise il dato reale va preso come “Milano”, in quanto la lista in questione parte con indice zero.
E gli scarni commenti?
Eccoli, finalmente. Le due funzioni personali CellaCondivisa e RiferimCella servono a individuare se una certa cella contiene o meno una stringa condivisa e, rispettivamente, a estrarne la coordinata. Sono entrambe frutto di un mio laborioso trattamento stringhe della proprietà Xml di un oggetto nodo, quella che ne fornisce tutte le informazioni e… non posso dire altro in merito (v. comunque il prossimo paragrafo, relativo all’esplorazione di attributi).
Ma cos’è un nodo? In gergo XML o, più esattamente, delle funzionalità DOM offerte da MS XML v6 (o v5) è un oggetto IXMLDOMNode, facente parte di un certo elenco (di nodi) IXMLDOMNodeList. In pratica sono nodi tutti i babbi, figli e nipoti che compongono la struttura gerarchica di un documento XML. Li si può spazzolare coi cicli For Each Nodo in NodiXML.. Next presenti nella nostra macro. Prima occorre impostare un nuovo DOMDocument e caricare, con il suo apposito metodo Load, il file .xlm desiderato e, subito dopo, settare in una qualche variabile NodiXml (di tipo IXMLDOMList) i nodi voluti, filtrati con un opportuno filtro Xpath.
La qual cosa in VBA si compie con un SelectNodes applicato al documento XML. E qui torna a fagiolo, l’indispensabile studio, chi non l’avesse fatto, di tale basilare strumento del mondo XML, qui mi limito a ripetere una sola istruzione:
Set NodiXml = DocXml.selectNodes("//sheetData/row/c")
Fugacemente: la doppia barra (//) fa saltare direttamente al nodo, passando sulla testa di genitori e antenati (per l’esattezza, // dà adito ai nodi di tipo specificato a qualunque livello si trovino, sotto la radice). Comunque nel caso precedente andava bene pure “//c” o persino “/c”... (studiate l’Xpath, gente).
Concludo citando le due proprietà Text e Xml di un nodo, la seconda già accennata, mentre la prima restituisce la stringa che esso racchiude.
Infine non mi resta che invitare iI paziente lettore all’analisi fai-da-te del funzionamento del programmetto... Buon divertimento!
Nota bene – L’oggetto IXMLDOMNodeList (lista dei nodi) non gode della proprietà Count, pertanto l’adozione di cicli For Each... Next si direbbe inevitabile? Così pensavo in un primo tempo, poi ho scoperto che in questo caso la proprietà è Length. La distinzione (sottile) deriva dal fatto che una IXMLDOMNodeList NON è un insieme ed è indicizzata a partire da zero. Pertanto un ciclo con indice relativo a una certa MiaListaNodi sarà del tipo:
For i = 0 To MiaListaNodi.Length - 1 . . . Next i
RICORDARSENE!
Dimenticavo. Una cosa cui molti avranno già pensato è che la routine discussa potrebbe essere sfruttata in un programma più ampio volto a esaminare in sequenza i dati essenziali di una serie di fogli di file distinti residenti in una certa directory, con opportune elaborazioni intermedie e conclusive. L'OOXML si presta a questo e altro (tra cui anche modifiche d'ogni tipo), occorre solo un po' di fantasia!
Un’alternativa per ottenere attributi di un nodo
Si considerino le seguenti routine, la prima volta ad esplorare ogni tipo di nodo, con la seconda che la sfrutta su diverse tipologie.
Sub EsploraNodo(FileXML As String, Nodo As String)
Dim DocXml As New DOMDocument
Dim NodoXml As IXMLDOMNode
Dim NodiXml As IXMLDOMNodeList
Dim TestoNodi As String
DocXml.async = False
DocXml.Load (FileXML)
Set NodiXml = DocXml.SelectNodes(Nodo)
Dim i As Integer
For Each NodoXml In NodiXml
'Debug.Print NodoXml.Text 'Servita per debug
'Set NodoXml = NodoXml.ChildNodes(0) 'Dà il primo nodo figlio: NON SERVE...
TestoNodi = TestoNodi & NodoXml.Text & vbLf
i = i + 1
Next
MsgBox TestoNodi, vbInformation, "Totale = " & i
'Segnala un elenco di nodi in una finestra MsgBox
End Sub
Sub ProvaEsploraNodo()
Dim fXml As String
fXml = "C:\CartMioFoglio\xl\worksheets\sheet1.xml"
EsploraNodo fXml, "//c[@r='C6']" 'Dà sia la FORMULA che
il VALORE del nodo con attributo r='C6'
EsploraNodo fXml, "//c[@r]" 'Segnala TUTTI i valori dei nodi c
Dotati dell’attributo r (nel nostro caso TUTTI quanti)
EsploraNodo fXml, "//c/@r" 'Segnala tutti gli attributi r (rifer. celle)
EsploraNodo fXml, "//c[@t='s']" 'tutti i nodi con argomento t='s'
EsploraNodo fXml, "//c/@t" 'tutti gli attributi dei nodi con argomento t:
End Sub
La routine ProvaEploraNodo sfrutta la Sub EsploraNodo con diverse casistiche, che invito a sperimentare e... meditare. Concentriamoci piuttosto sull’istruzione in grassetto. Quella che la precede usa il filtro Xpath “//[@r], un filtro di fatto fasullo, non solo perché non specifica nessuna condizione, ma in quanto tutti i nodi c possiedono l’attributo r, per cui sono indicati tutti indistintamente i valori dei nodi c stessi (al contrario del filtro “//c[@r=’C6’]” che individua il nodo della cella dotata di riferimento C6.
Molto interessante (e a mio avviso non a tutti ben nota) è l’istruzione grassettata: un filtro come “//c/@r” ossia omettendo le parentesi quadre restituisce l’attributo, come se fosse un nodo (un nodo particolare, che non ammette figli). Nel nostro caso otteniamo così l’elenco delle coordinate delle celle.
Tale tecnica avrei potuto utilizzarla anche sopra, ma ho preferito ricorrere alle mie funzioni RiferimCella e CellaCondivisa perché partendo dalla proprietà Xml del nodo mi ha permesso di avere due piccioni con una sola fava-nodo, evitando di innescare due loop For Each... Next.
Un’ultima annotazione, relativa alla proprietà Attributes(indice), sulla carta allettante, in quanto sembra promettere di restituire gli attributi del nodo. Tuttavia non c’è stato verso di applicarla e l’Help specifico non mi pare chiaro.
Se qualche superesperto che la sa più lunga di me mi può aiutare me lo comunichi all’indirizzo e-mail seguente:
ggiaccaglini@msn.com.
Eureka! Pensa e ripensa ho trovato la soluzione…
Come applicare la (fantomatica) proprietà Attributes non l’ho scoperto (né qualcuno me l’ha segnalato). In compenso mi sono reso conto che la cosa più semplice e organica consiste nell’applicare opportuni filtri XPath, per selezionare nodi relativi agli attributi che ci interessano. Per la precisione si tratta delle due istruzioni in grassetto nella nuova Sub RicostruzDati che ad ogni buon conto anticipo qui sotto:
Set NodiXmlRif = DocXml.selectNodes("//sheetData/row/c[@t='s']/@r")
Set NodiXmlVal = DocXml.selectNodes("//sheetData/row/c[@t='s']/v")
Il Filtro? E espresso dalla condizione [@t=’s’] cui avevo già pensato (v. sopra), alla quale però – ecco l’eureka! – ho aggiunto /@r e, rispettivamente, /v ottenendo in tal modo le coordinate di cella (attributo r) e i valori (la tag <v> “figlia” delle <c>, limitatamente alle celle i cui valori v sono da prendere come indici delle sharedStrings.
La istruzioni seguenti ci danno riferimenti e valori di tutte le celle, indiscriminatamente (dato che qui manca il filtro [@t=’s’]:
Set NodiXmlRif = DocXml.selectNodes("//sheetData/row/c/@r")
Set NodiXmlVal = DocXml.selectNodes("//sheetData/row/c/v")
Ed ecco la routine rivisitata, che fa a meno delle sudate funzioni personali:
Sub RicostruzDati(CartXML As String)
Dim DocXml As DOMDocument
Dim NodiXmlCond As IXMLDOMNodeList 'Stringhe condivise
Dim VettStrCond() As String 'Integer NON va bene...
Dim NodiXmlRif As IXMLDOMNodeList 'Rifer. a celle con stringhe condiv.
Dim NodiXmlVal As IXMLDOMNodeList 'Valori-indici di stringhe condiv.
Dim NodoXml As IXMLDOMNode
Set DocXml = New DOMDocument
DocXml.async = False 'inibisce sincronizzazione DOM/origine
'Carica stringhe condivise in un vettore (unico modo per
'poterle utilizzare successivamente)
DocXml.Load ("" & CartXML & "\xl\sharedStrings.xml")
Set NodiXmlCond = DocXml.selectNodes("//t")
Dim i As Integer, indStr As Integer
Dim LungNodi As Integer
LungNodi = NodiXmlCond.Length
ReDim VettStrCond(LungNodi)
For i = 0 To LungNodi - 1
VettStrCond(i) = NodiXmlCond(i).Text
Next
'Caricamento indiscriminato dei valori nelle celle del foglio
DocXml.async = False 'inibisce sincronizzazione DOM/origine
DocXml.Load ("" & CartXML & "\xl\worksheets\sheet1.xml")
'Lista nodi degli attributi @r e lista nodi dei valori
Set NodiXmlRif = DocXml.selectNodes("//sheetData/row/c/@r")
Set NodiXmlVal = DocXml.selectNodes("//sheetData/row/c/v")
For i = 0 To NodiXmlRif.Length - 1
Range(NodiXmlRif(i).Text) = NodiXmlVal(i).Text
Next
'Liste dei nodi e dei rispettivi valori che puntano
'a sharedString con la condizione [@t='s']
Set NodiXmlRif = DocXml.selectNodes("//sheetData/row/c[@t='s']/@r")
Set NodiXmlVal = DocXml.selectNodes("//sheetData/row/c[@t='s']/v")
MsgBox "Ora carico le stringhe condivise...", vbInformation, ""
For i = 0 To NodiXmlRif.Length - 1
With Range(NodiXmlRif(i).Text)
.Value = VettStrCond(NodiXmlVal(i).Text)
'Evidenzia con uno sfondo le celle contenenti stringhe,
'scenografia che prescinde dai veri formati, NON ricostruiti
With .Interior
.ThemeColor = xlThemeColorAccent6
.TintAndShade = 0.799981688894314
End With
End With
Next
End Sub
Sub ProvaRicostrDati()
RicostrDati "C:\CartMioFoglio"
End Sub
Conludo con avare annotazioni.
- Ho usato la proprietà Length di un elenco nodi (oggetto IXMLDOMNodeList) che ce ne dà il numero (equivale al Count dei normali insiemi, non posseduta dagli elenchi di nodi, distinzione sottile che mi ha fatto perdere tempo; acc.) di conseguenza sono ricorso a cicli For i = 0 To... ... Next anziché a loop For Each... Next.
- Ho esordito col caricamento delle stringhe condivise in un vettore, previo caricamento (Load) del file sharedStrings.xml. Si noti che tale vettore era inevitabile volendo sfruttare lo stesso (come dire? “spazio documentario”) DOMDocument sia per le SharedString che per l’intero foglio1 (sheet1.xml).
- Come nella precedente versione, dapprima si caricano tutti i valori nelle celle, poi si inseriscono le stringhe là dove occorre.
L’istruzione MsgBox “Ora carico le stringhe...” qui mira a mostrare all’utente la situazione dopo la prima fase. Ovviamente la si può, a regime, eliminare.
?>
?>