LUA è un linguaggio di scripting che ha guadagnato una certa popolarità nel corso degli anni grazie al suo interprete leggero, al supporto di concetti di programmazione OO ma soprattutto alla interoperabilità con .NET. Nel seguito una breve raccolta di riferimenti su come ho utilizzato LUA per manipolare dei file XML.
Ambiente di sviluppo
Ho deciso di raccogliere queste brevi noti per velocizzare il processo di startup di utilizzo del linguaggio di scripting. Intanto abbiamo due modi per eseguire script LUA:
- Standalone con l’installazione di un interprete, limitandoci all’ambiente Windows è sufficiente scaricare l’ultimo installer del progetto “luaforwindows” (link).
- Dalla propria applicazione, supponendo di avere a che fare con una applicazione .NET aggiungendo il riferimento all’assembly luanet (link). E’ questa forse la modalità più interessante perchè aggiungengo l’interprete embedded nella propria applicazione è chiaramente possibile configurare l’esecuzione di script esterni, quindi modificare o aggiungere funzionalità a posteriori senza la necessità di toccare il progetto originale.
La documentazione relativa alla sintassi del linguaggio è invece disponibile direttamente dal sito lua.org (link).
Manipolazione file XML con LUA
Considerando lo script standalone, attraverso la direttiva “require” è possibile aggiungere dei riferimenti a librerie LUA esterne, cominciamo caricando “luanet” ed utilizzando poi il metodo statico “load_assembly” per caricare le dll .NET che ci interessano. Dietro le quinte, il meccanismo di interoperabilità COM permetterà all’interprete LUA di istanziare ed utilizzare oggetti del framework .NET.
require "luanet" luanet.load_assembly("System.Xml") luanet.load_assembly("System.IO") File = luanet.import_type("System.IO.File") Directory = luanet.import_type("System.IO.Directory") XmlDocument = luanet.import_type("System.Xml.XmlDocument") XmlNode = luanet.import_type("System.Xml.XmlNode") XmlAttributeCollection = luanet.import_type("System.Xml.XmlAttributeCollection") XmlAttribute = luanet.import_type("System.Xml.XmlAttribute") XmlNamespaceManager = luanet.import_type("System.Xml.XmlNamespaceManager")
In questo snippet abbiamo preparato il campo alla manipolazione attraverso gli oggetti .NET di uso comune, ad esempio la classe XmlDocument che implementa il DOM oppure le classi File e Directory. Piccola nota sulla sintassi del linguaggio: i metodi statici richiedono l’utilizzo del punto singolo “.” (ad esempio File.Open() oppure Directory.GetCurrentDirectory()) mentre i metodi degli oggetti richiedono la notazione a due punti (es: xml_doc:Load()).
base_path = Directory.GetCurrentDirectory() xml_doc = XmlDocument() print("Load file.xml and update it") xml_doc:Load(base_path .. "\\file.xml") updateFile( xml_doc ) xml_doc:Save(base_path .. "\\file.xml")
La creazione dell’oggetto xml_doc è banalmente effettuata attraverso il costruttore XmlDocument, è possibile utilizzare direttamente i metodi mentre per le proprietà bisogna invocare i setter e getter associati, nell’esempio che segue alcune cose interessanti:
- Viene utilizzato un namespace definito nel file xml, deve quindi essere instanziato il relativo oggetto XmlNamespaceManager da utilizzare sulle successive chiamate di selezione dei nodi.
- L’utilizzo del formato di selezione dei nodi tramite XPath (la stringa parametri delle chiamate a SelectSingleNode
- L’utilizzo della proprietà “Attributes”, attraverso il metodo “get_Attributes()”. Come regola generale, ogni proprietà deve essere sostituita nello script dall’utilizzo dei metodi set_nomeProprietà e get_nomeProprietà
function updateFile(xml_doc) local nsmgr = XmlNamespaceManager(xml_doc:get_NameTable()) nsmgr:AddNamespace("def", "mynamespace") -- XmlNode selection if (xml_doc:SelectSingleNode("def:NODO[@Name = \"AttributoEsempio\"]", nsmgr) == nil) then -- DO STUFF end -- Change attribute example local equipmentNode = xml_doc:SelectSingleNode("def:EACB/def:EQUIPMENTS/def:EQUIPMENT", nsmgr) equipmentNode:get_Attributes():GetNamedItem("SimuName"):set_Value("127.0.0.1") end
Esempio di utilizzo di ICollection
Non essendo pratico di LUA ho avuto qualche difficoltà ad agire su proprietà che ritornano collezioni, molto semplicemente l’operazione può essere eseguita nel modo seguente:
function collection_example(xml_node) local child_nodes = root_node:get_ChildNodes() if child_nodes then local num_child = child_nodes:get_Count() if (num_child > 0) then for i=0,(num_child-1),1 do if child_nodes:Item(i) then -- DO SOMETHING on child_nodes:Item(i) end end end end end
Che dire infine.. detesto i linguaggi non tipizzati !