Sei sulla pagina 1di 11

VBJ n.

64 luglio-agosto 2005

XML Class Generator


di Vito Vessia
Come utilizzare il potente motore di scripting di Windows per produrre ed istanziare classi ` COM completamente costruite a runtime, per usufruire di tante interessanti potenzialita

Vito Vessia ` E cofondatore della codeBehind S.r.l. (http://www.codeBehind.it), una software factory di applicazioni enterprise, web e mobile, dove progetta e sviluppa applicazioni e framework in .NET, COM(+) e Delphi occupandosi degli aspetti architettura` li. E autore del libro Programmare il cellulare, Hoepli, 2002, sulla programmazione dei telefoni cellulari connessi al PC con protocollo standard AT+

pubblicato su WWW.INFOMEDIA.IT stampa digitale da Lulu Enterprises Inc. stores.lulu.com/infomedia


Infomedia
` Infomedia e limpresa editoriale che da quasi venti anni ha raccolto la voce dei programmatori, dei sistemisti, dei professionisti, degli studenti, dei ricercatori e dei professori dinformatica italiani. Sono pi` di 800 gli autori che hanno realizzato per le teu state Computer Programming, Dev, Login, Visual Basic Journal e Java Journal, molte migliaia di articoli tecnici, presentazioni di prodotti, tecnologie, protocolli, strumenti di lavoro, tecniche di sviluppo e semplici trucchi e stratagemmi. Oltre 6 milioni di copie distribuite, trentamila pagine stampate, fanno di questa impresa la pi` grande ed u inuente realt` delleditoria specializzata nel campo della a programmazione e della sistemistica. In tutti questi anni le riviste Infomedia hanno vissuto della passione di quanti vedono nella programmazione non solo la propria professione ma unattivit` vitale e un vero a divertimento. ` Nel 2009, Infomedia e cambiata radicalmente adottando ` un nuovo modello aziendale ed editoriale e si e organizzata attorno ad una idea di Impresa Sociale di Comunit` , a partecipata da programmatori e sistemisti, separando le attivit` di gestione dellinformazione gestite da un board a comunitario professionale e quelle di produzione gesti` te da una impresa strumentale. Questo assetto e in linea con le migliori esperienze internazionali e rende Infomedia ancora di pi` parte della Comunit` nazionale degli u a sviluppatori di software. ` Infomedia e media-partner di manifestazioni ed eventi in ambito informatico, collabora con molti dei pi` imporu tanti editori informatici italiani come partner editoriale e fornitore di servizi di localizzazione in italiano di testi in lingua inglese.

Limpaginazione automatica di questa rivista e realizzata al ` 100% con strumenti Open Source usando OpenOffice, Emacs, BHL, LaTeX, Gimp, Inkscape e i linguaggi Lisp, Python e BASH

For copyright information about the contents of Visual Basic Journal, please see the section Copyright at the end of each article if exists, otherwise ask authors. Infomedia contents is 2005 Infomedia and released as Creative Commons 2.5 BY-NC-ND. Turing Club content is 2005 Turing Club released as Creative Commons 2.5 BY-ND. Le informazioni di copyright sul contenuto di Visual Basic Journal sono riportate nella sezione Copyright alla ne di ciascun articolo o vanno richieste direttamente agli autori. Il contenuto Infomedia e 2005 Infomedia ` e rilasciato con Licenza Creative Commons 2.5 BY-NCND. Il contenuto Turing Club e 2005 Turing Club e ` rilasciato con Licenza Creative Commons 2.5 BY-ND. Si applicano tutte le norme di tutela dei marchi e dei segni distintivi. ` E in ogni caso ammessa la riproduzione parziale o totale dei testi e delle immagini per scopo didattico purch e vengano integralmente citati gli autori e la completa identicazione della testata. Manoscritti e foto originali, anche se non pubblicati, non si restituiscono. Contenuto pubblicitario inferiore al 45%. La biograa dellautore riportata nellarticolo e sul sito www.infomedia.it e di norma quella disponibi` le nella stampa dellarticolo o aggiornata a cura dellautore stesso. Per aggiornarla scrivere a info@infomedia.it o farlo in autonomia allindirizzo http://mags.programmers.net/moduli/biograa

I MITI

XML Class Generator


Come utilizzare il potente motore di scripting di Windows per produrre ed istanziare classi COM completamente costruite a runtime, per usufruire di tante interessanti potenzialit
di Vito Vessia
Generator

a metafora dellXML come linguaggio dei linguaggi e come mezzo universale di interscambio delle informazioni stata ironicamente descritta nella premessa di [1] come XML has replaced Java, Design Patterns, and Object Technology as the softwares industry solution to world hunger Al di l delle battute indubbia limportanza che ha assunto ormai questo semplice e quasi banale formato di denizione dei dati nel mondo della comunicazione moderno. XML, nato come linguaggio di formattazione dei contenuti, direttamente discendente da SGML, si poi imposto come esperanto per qualsiasi informazione si volesse produrre e distribuire. Al punto che, da completo sistema di denizione dei dati e quindi del contenuto, diventato anche il linguaggio di denizione dei contenitori dei dati e quindi come vero e proprio protocollo applicativo. Un esempio per tutti certamente SOAP, che usa proprio XML per denire un completo protocollo applicativo per realizzare un RPC (Remote Procedure Call). Altrettanto straordinario stato limpatto che hanno avuto i vari linguaggi di scripting nel mondo della programmazione: hanno unito la potenza dei linguaggi di sviluppo tradizionali alla semplicit di scrittura e di deployment, in modo che qualsiasi ambito applicativo potesse sfruttarne al massimo le peculiarit senza che fosse dotato di una complessa infrastruttura di compilazione e di runtime. Scopo di questo articolo sar presentare uno studio su un possibile utilizzo dello scripting per realiz-

zare un generatore automatico di classi VBScript che rimappino (e ne facciano da proxy) la struttura di un le XML qualunque, purch dotato di schema (interno o esterno). In realt la struttura del generatore di classi semplice, ma alquanto complessa da spiegare in modo astratto e sintetico in questa premessa. Quindi non vi resta che leggere il seguito

Gli schemi XML


Una interessante caratteristica dellXML la possibilit di vericare la correttezza sintattica di un certo le rispetto ad un template standard di riferimento detto schema. Infatti lXML di per s un formato piuttosto rigido, nel senso che deve essere garantita la correttezza sintattica, che prevede una lunga serie di regole da rispettare, perch il le XML si possa denire well formed. Queste vanno dal chiudere ogni tag che viene aperto, alluso degli apici per i valori degli attributi, alla presenza di un solo nodo padre per ciascun le e tanto altro ancora. Non sar argomento di questo articolo spiegare queste regole; la Rete e le librerie sono ormai sature di materiale sullXML. Un buon consiglio per la lettura certamente [1].

Vito Vessia co-fondatore della codeBehind S.r.l. (http: //www.codeBehind.it), una software factory di applicazioni enterprise, web e mobile, dove progetta e sviluppa applicazioni e framework in .NET, COM(+) e Delphi di cui si occupa degli aspetti architetturali. autore del libro Programmare il cellulare, Hoepli, 2002, sulla programmazione dei telefoni cellulari connessi al PC con protocollo standard AT+.Pu essere contattato tramite e-mail allindirizzo vvessia @infomedia.it.

52

VBJ N. 64 - Luglio/Agosto 2005

I MITI
La verica sintattica per non tutto, ma si pone la necessit di denire un formato che descrivesse le regole semantiche a cui il le deve adeguarsi perch possa essere compreso anche da chi non ne lautore. Proviamo ad immaginare di voler denire un formato standard di fatture da condividere con i nostri clienti e fornitori, e per far ci vogliamo basarci su XML; dobbiamo altres specicarne la logica (es. un nodo di testata che si chiama HEADER, che contiene alcuni attributi come Number, Year e altri ancora, poi devono esserci uno o pi nodi di tipo ROW e cos via). ment), con la struttura di ogni attributo che contiene (xs:attribute) compresi i tipi e le eventuali constraint (es. use=required). Lelenco dei nodi at, cio non presenta la tipica struttura gerarchica dei le XML che lega i nodi padre ai gli: questa gerarchia invece denita attraverso la presenza dei nodi xs:sequence deniti nei nodi xs:element. Il principio di funzionamento molto semplice: per vericare che il le XML sia semanticamente corretto rispetto allo schema di riferimento deve soddisfare tutte le regole di questultimo. Questa verica di correttezza sarebbe un po troppo onerosa, anche se non impossibile, se realizzata a mano afdandosi solo al nostro colpo docchio, ma in realt il compito realizzato egregiamente da un buon parser XML di ultima generazione. Si intende cio un parser in grado di manipolare e di comprendere gli schema XML di ultima generazione deniti dal World Wide Web Consortium [2]. Nel nostro esperimento utilizzeremo il nuovo e potente parser Microsoft XML Core Services (MSXML) 4.0. Questo componente racchiude al suo interno un classico DOM (Document Object Model) cio un oggetto in grado di produrre un completo object model a partire da un le XML, un SAX (Simple API for XML) cio un componente che non conserva in memoria lintera struttura del le XML ma scatena degli eventi durante il parsing dello stesso.

XML di per s un formato piuttosto rigido

Lo schema XML serve proprio a questo. In realt gli schemi sono uninvenzione recente per lXML: inizialmente il loro compito era svolta dai DTD (Document Type Denition) dalla logica molto simile. Gli schemi hanno numerosi vantaggi: sono XML essi stessi, supportano lereditariet e sono molto pi potenti e completi. Osserviamo ad esempio il Listato 1. Lo schema di riferimento del le XML appena mostrato visibile nel Listato 2. In realt lesempio stato privato di numerosi attributi e di alcuni nodi per ridurne loccupazione tipograca, ma rende lidea. Per consultare il codice Visual Basic si rimanda al sorgente che accompagna larticolo, scaricabile liberamente dal sito ftp: //ftp.infomedia.it. Possiamo notare come il le XML presenti un riferimento allo schema a cui soggiace (Untitled2.xsd) e che lo schema non sia altro che un conFigura 1 La gerarchia di oggetti dello Schema Object Model tenitore di tutti i tipi di nodi presenti nel le (nodi xs:ele-

N. 64 - Luglio/Agosto 2005

VBJ

53

I MITI
certamente pi performante del DOM anche se richiede una maggiore elaborazione e offre meno servizi. La vera novit della versione 4.0 invece la presenza del SOM (Schema Object Model): un componente in grado di produrre un object model a partire da uno schema XML (Riquadro 1). Data la natura completamente plain text del formato XML, sarebbe possibile editare, creare e modicare questi le e gli schemi associati direttamente col Notepad, ma ci si rende conto ben presto che questo compito ha del disumano. Ed soprattutto vero quando si usano pesantemente gli schemi.

Cosa centra lo scripting?


Giunti a questo punto della lettura vi chiederete cosa centra lo scripting in tutto questo. In effetti non centra apparentemente nulla, se non fosse che si possono realizzare interessanti applicazioni dallabbinamento della tecnologia di scripting ActiveScript di Microsoft e lXML con schema.

Realizzare unintera applicazione in VBScript probabilmente poco pratico


Infatti il disaccoppiamento tra schema XML e dati XML ricorda da vicino la metafora OOP della classe e delloggetto: la classe la denizione formale delle regole e dei comportamenti di un particolare aggregato strutturale di dati, loggetto invece la stessa classe che prende vita e va in esecuzione su un particolare set di dati che corrisponde al prototipo dellaggregato detto in precedenza. Cos se la denizione dei dati e dei comportamenti sugli stessi (la classe) unica per una particolare tipologia applicativa, le sue istanze (gli oggetti) speciche basate su set di dati differenti possono essere invece numerose e senza relazioni tra loro. Cos, nel caso specico, possiamo immaginare di produrre un set di classi, un intero object model, a partire da uno schema XML. Fatto questo saremo in grado di produrre istanze di queste classi che si basano su le XML differenti, purch soggiacciano allo schema comune da cui sono state generate le classi. Vediamo un esempio di classi VBScript che permettono di navigare il le XML dati.xml presente nella directory dei sorgenti che accompagnano larticolo:
codice VBScript Class vCRM Private m_Dictionary Private m_OnStartup Private m_VERSION Private m_AUTHOR

Riquadro 1

Schema Object Model (SOM)

Il SOM basato sullabstract W3C riferito in [3]. Esso prevede sostanzialmente laccesso agli elementi contenuti in uno schema XML (propriet, dichiarazioni, relazioni tra elementi). quindi un set navigabile di classi che riflettono direttamente le specifiche W3C XSD (Schema Denition Language). In Figura 1 riportata la gerarchia delle interfaccia implementate in questo componente. Si pu notare come appunto la gran parte delle interfacce esposte sia discendenti da altre interfacce. Vediamo subito un esempio di come si carica uno schema XML nel SOM. Lesempio tratto dalla documentazione a corredo del MSXML 4 [5]:

Dim oSchemaCache as New IXMLDOMSchemaCollection2 Dim oSchema as ISchema Dim nsTarget as String nsTarget = http://www.sample.com/sampletarget oSchemaCache.add nsTarget, PO.xsd Set oSchema = oSchemaCache.getSchema(nsTarget)
Viene dunque istanziato un oggetto IXMLDOMSchemaCollection2 e di questo viene invocato il metodo Add per aggiungere un nuovo schema (PO.xsd) con namespace http://www.sample.com/sampletarget alla collezione degli schema contenuta nelloggetto appena istanziato. A questo punto possibile farsi ritornare lo schema appena inserito grazie al metodo getSchema, ma questa volta nella forma di interfaccia ISchema che linterfaccia pi importante del SOM perch lunica che permette di esplorare le caratteristiche dello schema. Esistono comunque decine di interfacce diverse, alcune delle quali verranno adoperate nel proseguo di questo articolo. In generale si rimanda alla consultazione della ricca documentazione allegata al componente di Microsoft per una completa comprensione perch non scopo di questa esposizione esplorare pienamente le caratteristiche del componente, cosa che richiederebbe da solo uno o pi articoli interi.

54

VBJ N. 64 - Luglio/Agosto 2005

I MITI

Listato 1

Un file XML

<?xml version=1.0?> <vCRM AUTHOR=Vito Vessia mlns:xsi=http://www.w3.org/2001/XMLSchema-instance xsi:noNamespaceSchemaLocation=F: \ScriptClasses\XMLClasses\disk\Untitled2.xsd> <ORDER DONE_NODE=FALSE CODE_SITE=JRHOLD CODE_CORRESPONDENT=ARDILLO CODE_INVOICE_CUST=029900 CODE_SHIP_CUST=029900 CODE_ORDER_TYPE=% CODE_FAIR=COD_FIERA CODE_PL_SOFA=AJ CODE_PL_ACCESS=EF CODE_CURRENCY=ITL[~] COD_CAUS_COMM=G03 COD_CAUS_PROD=MAG CODE_DOC_TYPE=003> <HEADER_REMARK HRMRK_CODE_SITE=JRHOLD HRMRK_PROGRES=1 HRMRK_CODE=OCMC HRMRK_REMARK=Maximum[~]Care HRMRK_ON_CREDITS=0 HRMRK_ON_DDT=1 HRMRK_ON_LABEL=0 HRMRK_ON_INVOICE=1 HRMRK_ON_OC=1/> <ROW CODE_DBALT=031615 CODE_PL=AJ ROW=1 NUM_REVIS_ROW=0 ORDER_SET=2 PO= CODE_PART=073504224> <ROW_REMARK RRMRK_CODE_SITE=JRHOLD RRMRK_PROGRES=2 RRMRK_CODE=OCMC RRMRK_REMARK=Maximum[~]Care/> <COVER CODE_COVER=15001011 CODE_QUEST=Z0110001 FLG_ROW_MODIFIED= PROGR_RIV=1 NUM_REVIS_COVER=0 CVR_CODE_SITE=JRHOLD/> </ROW> </ORDER> </vCRM>

Public CUSTOMER Public ORDER Private Sub Class_Initialize() set m_Dictionary = CreateObject (Scripting.Dictionary) m_OnStartup = True set CUSTOMER = CreateObject (Scripting.Dictionary) set ORDER = CreateObject (Scripting.Dictionary) End Sub Private Sub Class_Terminate() set m_Dictionary = Nothing set CUSTOMER = Nothing set ORDER = Nothing End Sub Public Property Get Attributes() Set Attributes = m_Dictionary .Items End Property Public Property Get AUTHOR() AUTHOR = m_AUTHOR End Property Public Property Let AUTHOR(valueField) m_AUTHOR = valueField End Property End Class

Quella appena mostrata la classe di navigazione del nodo vCRM del le XML in questione (le gi parzialmente riportato in precedenza in questo articolo). Osservando le funzioni in essa contenute, possiamo notare la presenza di property let/ get per accedere alla propriet AUTHOR che, nel le XML, lattributo AUTHOR. Esister quindi una coppia di questi metodi nella classe per ogni attributo nel le XML dorigine. Possiamo inoltre notare la presenza della property get Attributes che rappresenta la collezione di tutti gli attributi allinterno della classe, in modo che vi si possa accedere per indice e per chiave e, soprattutto, si possa applicare lenumerazione COM (la for each). Cos, per accedere allelemento AUTHOR potremmo invocare:
codice VBScript Dim oCRM Set oCRM = New vCRM oCRM.AUTHOR = Vito Vessia Msgbox oCRM.AUTHOR

oppure accedere in modo simbolico alla propriet:


MsgBox oCRM.Attributes(AUTHOR)

N. 64 - Luglio/Agosto 2005

VBJ

55

I MITI

Listato 2

Lo schema relativo al file del Listato 1

<?xml version=1.0 encoding=UTF-8?> <xs:schema xmlns:xs=http://www.w3.org/2001/XMLSchema elementFormDefault=qualified> <xs:element name=vCRM> <xs:complexType> <xs:sequence> <xs:element ref=ORDER/> </xs:sequence> <xs:attribute name=AUTHOR type=xs:string use=required/> <xs:attribute name=DATE type=xs:string use=required/> </xs:complexType> </xs:element> <xs:element name=ORDER> <xs:complexType> <xs:sequence> <xs:element ref=HEADER_REMARK maxOccurs=unbounded/> <xs:element ref=ROW maxOccurs=unbounded/> </xs:sequence> <xs:attribute name=DONE_NODE type=xs:string use=required/> <xs:attribute name=CODE_SITE type=xs:string use=required/> </xs:complexType> </xs:element> </xs:schema>

e scorrerle tutte semplicemente con:


codice VBScript Dim lAttribute For Each lAttribute in oCRM.Attributes Msgbox lAttribute Next

Public Property Get TOT_SIT() TOT_SIT = m_TOT_SIT End Property Public Property Let TOT_SIT(valueField) If Not IsNumeric(0 & valueField) Then Err.Raise 1970, , Invalid number! End If m_TOT_SIT = valueField End Property End Class

La collezione degli attributi realizzata usando un Dictionary a livello di classe che viene istanziato nella Class_Initialize. Osservando per il nodo XML vCRM, possiamo notare che esso possiede alcuni nodi gli. Uno di questi ORDER, la cui classe, secondo il prototipo appena visto, dovrebbe essere come quella che segue (per ragioni di praticit in realt se ne riporta solo un estratto):
codice VBScript Class Order Private m_Dictionary Private m_OnStartup Private m_CODE_FAIR Private m_COD_CAUS_PROD Private m_TOT_SIT Public HEADER_REMARK Public ROW

Possiamo notare che la classe vCRM presentava una propriet ORDER. Questa rappresenta proprio la collezione dei nodi gli che, a tutti gli effetti, sono classi di tipo Order come quella appena mostrata. Questa collezione realizzata, ancora una volta, col solito Dictionary. Cos, se vogliamo accedere e stampare a video la propriet TOT_SIT del secondo nodo ORDER di una classe vCRM che fa da proxy al nostro XML vCRM, dobbiamo semplicemente scrivere:
Msgbox vCRM.ORDER(2).TOT_SIT

A sua volta, il nodo Order conterr altri nodi gli ripercorrendo la stessa gerarchia del le XML originario, cos da realizzare un mo-

56

VBJ N. 64 - Luglio/Agosto 2005

I MITI
dello ad oggetti. Nel caso specico i nodi gli di Order sono HEADER_REMARK e ROW e cos via no a completare la gerarchia (es. vCRM.ORDER(2).ROW(3). COVER(1).CODE_ QUEST). Si pu vedere quindi come possibile realizzare classi VBScript (o di scripting in generale in tecnologia Microsoft ActiveScripting) che ripercorra, in forma pi naturale ed immediata, la struttura di un le XML qualsiasi basandosi sullo schema a cui soggiace il le stesso. Sar cos possibile istanziare queste classi con i dati presenti nel le XML, infatti ogni attributo del nodo XML esposto nella classe in forma di property get/let che permetter quindi anche di assegnare i valori di queste propriet:
vCRM.ORDER(2).TOT_SIT = 3

IDispatch di COM come mostrato in [4]. Brevemente vediamo come utilizzare direttamente da Visual Basic tali classi grazie al potente Microsoft Script Control [6] che liberamente scaricabile dal sito di Microsoft e che semplica drasticamente luso della tecnologia ActiveScripting (il linguaggio usato sar VBScript, alternativamente si sarebbe potuto usare qualsiasi altro linguaggio di scripting installato):
codice VBA Dim oSC As Object lo Script Control Set oSC = CreateObject(MSScriptControl.ScriptControl) With oSC .AllowUI = True permesso luso di interfaccia utente negli script .Language = VBScript .UseSafeSubset = False non impone nessuna restrizione per la sicurezza End With a questo punto aggiungiamo il codice delle classi VBScript che effettuano il proxy dei nodi XML come visto in precedenza oSC.AddCode <codice della classe>

Realizzare unintera applicazione in VBScript probabilmente poco pratico e ancor meno interessante dal punto di vista dellimplementazione in una situazione reale. Si da il caso per che sia possibile utilizzare procuamente tali classi in applicazioni Visual Basic (VBA) o in generale in tutti quei linguaggi ad alto livello capaci di gestire le interfacce

Fatto questo possiamo procedere allistanziazione di queste classi VBScript e al loro uso allinterno di Visual Basic.

Listato 3

Assegnazione di valori agli attributi in base alle caratteristiche

For Each lAttribute In lComplex.Attributes CreateClassesFromXMLSchema = CreateClassesFromXMLSchema & vbCrLf & _ Public Property Get & lAttribute.Name & () & vbCrLf & _ & lAttribute.Name & = m_ & lAttribute.Name & vbCrLf & _ End Property & vbCrLf & _ & vbCrLf & _ Public Property Let & lAttribute.Name & (valueField) & vbCrLf & vbCrLf Set lProp = lAttribute Set lType = lProp.Type Select Case lType.itemType Case SOMITEM_DATATYPE_BYTE, SOMITEM_DATATYPE_DECIMAL, SOMITEM_DATATYPE_DOUBLE, _ SOMITEM_DATATYPE_FLOAT, SOMITEM_DATATYPE_INTEGER, SOMITEM_DATATYPE_LONG, SOMITEM_DATATYPE_SHORT: CreateClassesFromXMLSchema = CreateClassesFromXMLSchema & _ If Not IsNumeric(0 & valueField) Then & vbCrLf & _ Err.Raise 1970, , Invalid number! & vbCrLf & _ End If & vbCrLf Case SOMITEM_DATATYPE_DATE, SOMITEM_DATATYPE_DATETIME: CreateClassesFromXMLSchema = CreateClassesFromXMLSchema & _ If Not IsDate( & valueField) Then & vbCrLf & _ Err.Raise 2000, , Invalid date! & vbCrLf & _ End If & vbCrLf End Select

N. 64 - Luglio/Agosto 2005

VBJ

57

I MITI
Infatti, trattandosi di normali istanze di interfacce IDispatch per utilizzarle in Visual Basic sufciente ottenere il puntatore allistanza della classe stessa e assegnarlo ad un normale Object o Variant. Questo possibile sfruttando il potente metodo Eval dello ScriptControl (e dello scripting pi in generale) che permetta di valutare, facendosi ritornare il risultato in forma di variant, una qualsiasi espressione:
codice VBA Dim oCRM As Object Set oCRM = oSC.Eval (New vCRM)

A questo punto possiamo accedere a oMyOrder direttamente da codice Visual Basic esattamente come abbiamo fatto allinterno di VBScript:
codice VBA oCRM.AUTHOR = Vito Vessia Msgbox oCRM.AUTHOR

su nodi, sequence, ecc Perch non afdarsi quindi ad un generatore automatico di classi a partire da schema e di istanze delle classi create in precedenza a partire da le XML che soggiacciono allo stesso schema? Ebbene quello che andremo a realizzare, seppur in una forma non esaustiva. Il sorgente che accompagna larticolo presenta il progetto Visual Basic di un ActiveX Dll (CPXML Proxies.vbp) e di un progetto di esempio che utilizza tale engine (CPXMLProxyDemo.vbp) sul le XML mostrato nellarticolo. Il motore per generico e funziona su ogni XML con schema standard [3] e utilizza il nuovo SOM di MSXML 4 per produrre le classi a partire dagli schema e il DOM per produrre le istanze delle classi a partire dai nodi XML. Questo presenta solo due metodi pubblici. Il primo, CreateClassesFromXMLSchema, permette di produrre una gerarchia di classi VBScript a partire da uno schema XML:
codice VBA Public Function CreateClassesFromXMLSchema (XMLSchemaObject As Variant, _ Optional XMLSchemaRef As Variant) As String

Il Class Generator
Il meccanismo appena mostrato molto potente ma ha, nella forma in cui labbiamo mostrato, unutilit quasi nulla perch ci permette s di scrivere le nostre classi di proxy intorno ad un qualsiasi XML con schema, ma ci costringe comunque a scriverci manualmente, e per ogni differente schema, il codice di tali classi. Ci costringe inoltre, cosa ancora pi inutile, a istanziare tali classi e a popolarle secondo i dati presenti nel le XML in questione.

Le classi che andremo a produrre non perderanno nulla delle caratteristiche dello schema XML di cui vanno a realizzare il wrapper
A ben pensarci si tratterebbe di scrivere ogni volta sempre lo stesso codice perch gli schema XML si descrivono sempre nelle stesse modalit viste in precedenza e il popolamento delle istanze ancora pi banale perch non altro che un susseguirsi interminabile di for each

Il primo parametro rappresenta proprio lo schema da cui produrre le classi. di tipo variant perch potrebbe essere sia il nome del le o la url dello schema e sia direttamente loggetto ISchema, ed infatti vengono fatti i controlli del caso (una IsObject e una QueryInterface) per comprenderne la natura. Il secondo parametro passato invece per referenza e viene usato come parametro di tipo out, cio viene valorizzato in uscita; in pratica a questo viene assegnata listanza dello schema appena prodotto. Il valore di ritorno della funzione invece proprio il codice sorgente delle classi. A questo punto si procede scorrendosi la collection elements dello ISchema, ciascun elemento rappresenta un diverso nodo dei le XML sottoposti allo schema dato. Vediamo un estratto della classe
Dim Dim Dim Dim Dim lSchemaItem As MSXML2.ISchemaItem lAttribute As MSXML2.ISchemaItem lSchema As MSXML2.ISchema lElem As MSXML2.ISchemaElement lComplex As MSXML2.ISchemaComplexType

Dim lProp As MSXML2.ISchemaAttribute

58

VBJ N. 64 - Luglio/Agosto 2005

I MITI
Laltro metodo CreateInstanceFromXML. Esso banalmente scorre il le XML passato in input (XMLIssue)
Public Function CreateInstancesFromXML (XMLIssue As Variant, Optional ParentArray As Variant, Optional XMLSchemaRef As Variant, Optional SourceCode As String = ) As Object

Dim lType As MSXML2.ISchemaType For Each lSchemaItem In lSchema.elements CreateClassesFromXMLSchema = CreateClassesFromXMLSchema & _ Class & StrConv(lSchemaItem.Name, vbProperCase) & vbCrLf Set lElem = lSchemaItem If lElem.itemType = SOMITEM_ELEMENT Then Set lComplex = lElem.Type CreateClassesFromXMLSchema = CreateClassesFromXMLSchema & vbCrLf & _

A questo punto, per ogni classe che si va a creare, si procede con lenumerazione della collection Attributes del Complex ottenuto effettuando il casting delloggetto nodo (come si pu osservare nel codice appena mostrato). Nel codice specico vengono prodotti i membri privati della classe che conterranno i valori delle propriet/attributi di ogni nodo.
For Each lAttribute In lComplex.Attributes CreateClassesFromXMLSchema = CreateClassesFromXMLSchema & vbCrLf & _ Next Private m_ & lAttribute.Name

Il parametro XMLIssue rappresenta il nome o il nodo XML che si vuole trasformare in unistanza di una classe VBScript generata dallo schema a cui soggiace il nodo stesso. Vale infatti lo stesso discorso di ambivalenza visto per il parametro del metodo precedente.

Questo meccanismo si presta alla realizzazione di generatori automatici di codice


Gli altri parametri, invece, sono apparentemente poco signicativi ed infatti non verranno mai adoperati (per questo sono opzionali) in modo diretto; servono invece allinterno del metodo stesso perch si tratta di un metodo ricorsivo. Infatti la logica del metodo che, dal nodo XML appena passato (o dal nodo root del le XML se il parametro XMLIssue di tipo nodo) viene popolato unistanza. Per ogni nodo glio del nodo corrente viene invocato il metodo nuovamente per popolare le istanze di questi nodi gli e cos via. I tre altri parametri servono proprio per comunicare informazioni alle altre istanze di ricorsione del metodo in modo da velocizzare alcune operazione e donare uno stato al metodo.
Dim Dim Dim Dim Dim Dim Dim Dim lObject As Object lElement As MSXML2.IXMLDOMElement lDOM As MSXML2.DOMDocument40 lXSDFile As String lAttribute As MSXML2.IXMLDOMAttribute lSchema As MSXML2.ISchema lSchemaItem As MSXML2.ISchemaItem lSchemaElement As MSXML2.ISchemaElement

Stessa cosa, per sulla collection particles di contentModel del Complex. Essa conterr lelenco eventuale dei nodi gli del nodo corrente (ad es. il nodo vCRM ha come nodi glia Order e Customer).
For Each lAttribute In lComplex.contentModel.particles CreateClassesFromXMLSchema = CreateClassesFromXMLSchema & vbCrLf & _ Public & lAttribute.Name Next

Unaltro interessante pezzo di codice da osservare quello che permette di gestire le constraint a livello di property let di ogni attributo, cio di gestire correttamente le assegnazioni di valori agli attributi in base alle caratteristiche peculiari (tipo, lunghezza, ecc.) direttamente ereditate dalle informazioni di schema. Si tratta di unimplementazione molto parziale, ma rende lidea della potenza del meccanismo che si va ad implementare: le classi che andremo a produrre non perderanno nulla delle caratteristiche dello schema XML di cui vanno a realizzare il wrapper (Listato 3).

Dim lSchemaComplex As MSXML2.ISchemaComplexType

N. 64 - Luglio/Agosto 2005

VBJ

59

I MITI
Vediamo dunque il motore appena creato in azione in un esempio molto semplice in Visual Basic:
codice VBA Dim oProxy As Object Dim oObject As Object Set oProxy = CreateObject(CPXMLProxies.XMLProxy) Set oObject = oProxy.CreateInstancesFromXML(dati.xml) MsgBox oObject.Order(1).Row(2).Cover(1) .CodQuest

Dim lSchemaAttribute As MSXML2.ISchemaItem Dim lNodeList As MSXML2.IXMLDOMNodeList Dim lChildElement As MSXML2.IXMLDOMElement If SourceCode = Then ci troviamo al livello 0 della ricorsione e il sorgente delle classi non stato ancora prodotto e quindi viene prodotto in questa fase usando il metodo precedente (codice omesso, per lo studio del codice di questo metodo si rimanda ai sorgenti) Else un livello di ricorsione >0 e quindi il sorgente delle classi stato passato per referenza Set lSchema = XMLSchemaRef End If nuova istanza della classe wrapper del nodo corrente Set lObject = g_SC.Eval (new & lElement.nodeName)

Semplice e potente

Conclusioni
Abbiamo visto come realizzare un sistema di classi VBScript di wrapper a partire da un le XML con schema, riciclando il concetto di classe/istanza sullequivalente XML di schema/le. Le istanze di queste classi possono poi essere utilizzate a runtime dalle nostre applicazioni Visual Basic che quindi acquistano la capacit di gestire i complicati le XML con schema senza conoscere nulla di questo formato, senza ricorrere al potente ma un po complicato object model di DOM e senza neppure mantenere una referenza a MSXML 4. Il tutto stato generato facendo uso di un motore molto semplice scritto in Visual Basic, che quindi ci mette in salvo dalla riscrittura di queste classi VBScript ogni volta che cambia il formato dello schema e ci risparmia anche dal codice di popolamento delle istanze. Al di l della possibilit di mappare classi IDispatch su XML, questo meccanismo si presta alla realizzazione di generatori automatici di codice, magari a partire da template XML che li descrivono o ad altre applicazioni simili o diverse che possono trarre giovamento da questo singolare utilizzo. Si accettano consigli a riguardo

Per ogni propriet/attributo dellistanza viene invocata la property let per assegnare il valore preso dal nodo XML originale per quellattributo. La valorizzazione avviene per nome simbolico della propriet, grazie alla potente CallByName di Visual Basic:
For Each lSchemaAttribute In lSchemaComplex.Attributes CallByName lObject, lSchemaAttribute.Name,VbLet, _ lElement.Attributes.getNamedItem (lSchemaAttribute.Name).nodeValue lObject.Attributes.Add lSchemaAttribute.Name, _ lElement.Attributes.getNamedItem (lSchemaAttribute.Name).nodeValue Next

In questo brano di codice si pu invece osservare luso della ricorsione nel metodo: per ogni nodo glio di quello corrente viene invocato infatti lo stesso metodo.
For Each lSchemaAttribute In lSchemaComplex. contentModel.particles Set lNodeList = lElement.getElementsByTagName (lSchemaAttribute.Name) For Each lChildElement In lNodeList CreateInstancesFromXML lChildElement, CallByName(lObject, lChildElement. baseName, VbGet), lSchema, SourceCode Next Next

Bibliograa
[1] Don Box, A. Skonnard, J. Lam, Essential XML Beyond markup, Addison Wesley (2000) [2] I tipi di dati di SOAP, http://www.w3.org/ 2001/XMLSchema [3] XML Schema Part 0: Primer, http:// www.w3.org/ TR/xmlschema-0/ [4] Vito Vessia, Tecniche di osmosi nello scripting, DEV n. 95

60

VBJ N. 64 - Luglio/Agosto 2005