Manipolare file XML con LUA

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:

  1. Standalone con l’installazione di un interprete, limitandoci all’ambiente Windows  è sufficiente scaricare l’ultimo installer del progetto “luaforwindows” (link).
  2. 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:

  1. 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.
  2. L’utilizzo del formato di selezione dei nodi tramite XPath (la stringa parametri delle chiamate a SelectSingleNode
  3. 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 !

Lascia un commento