Hi, I couldn't find any existing source-code for serializing LUA tables to and from Xml using TinyXml, not even on Google, so here it is. ^-^ Feel free to use this in any way you want. I've written it for game development, so it is ment for easy storing/retrieving highscores, game-states etc. but also to take away parameters from programmers and pass them to artists in game development (by storing Xml-s in compressed streams). I haven't tested it yet on very large databases, please feel free to comment. :) I'll cut & paste the source below this message & attach it to this mail, hopefully it will get through the mailing list filter unharmed. ;) Good luck! Cheers, Hugo // 'data' / data_xml.cpp // // written for the Framework Engine ((c) 2004, 2005 by Framework Studios) // www.framework-studios.com // //------------------------------------------------------------------- // "data_xml.cpp" // Copyright (c) 2004, 2005 by Hugo F. Habets // All Rights Reserved // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this "data_xml.cpp" and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. //------------------------------------------------------------------- // // This 'data' code snippet connects the TinyXml library and LUA 5.0. // // For more information about TinyXml and Lua, please see: // // http://sourceforge.net/projects/tinyxml/ // http://www.lua.org/ // // The 'data' function in LUA can write a LUA table to a Xml file // and read a Xml-file generated with 'data' back to a LUA table. // // In C/C++, Xml data from any source (file, stream, etc.) when using the // same format 'data' uses, can be read to a LUA table with ReadTableFromXml. // // This code has been written with these goals: // // * Serialize LUA tables via Xml // * Load LUA tables from a file stream containing Xml // * Save the index and value types for string, boolean (value only), // number and table (value only) // * Possibility to combine multiple Xml sources into one table // // Syntax: // -- write the table MyTable to the file MyData.Xml // data(MyTable, "MyData.Xml") // // -- read the file MyData.Xml into the table MyTable // data("MyData.Xml", MyTable) // // Note: to read Xml data into a table, the table must already exist: // // -- read MyData.Xml into a new table called MyTable // MyTable = {} // data("MyData.Xml", MyTable) // // The type of the index is kept when it is a number (MyTable[1]) or a // string (MyTable.Name). // // The type of the value is kept when it is a number (MyTable.Value = 10), // a string (MyTable.Name = "Hugo"), a boolean (MyTable.Truth = true) or // a table (MyTable.Tree = {}). In the latter, the nested table should contain // actual values to be fully serialized. // // Other types (like userdata or function) are ignored, they may be in the // original table but will not be serialized through 'data' nor result in an error. // // Empty nested tables when saved might occur in the Xml, however will be // ignored on loading. // // Table hierarchy is kept (MyTable.Tree = {} MyTable.Tree.IsThisSaved = true). // // The exact order of value initialization might not be kept (which shouldn't // be a problem in virtually any case). // // This code uses the "I" and "V" attributes in Xml to identify the type of the // "I"ndex and "V"alue. These can be "S"tring, "N"umber, "B"oolean or "T"able. // Single characters are used to keep the Xml file-size relatively low. // However it should be easy to use full names for clearity, in that case note // this code as it is now only checks the first character to identify the type. // // Since eighter TinyXml or the Xml standard seems to have a problem when // indexing/tagging with a number, when indexed by number the number // will be preceeded by the letter 'n' (1.000000 becomes n1.000000). // // Example table (texture animation): // // Anim = {} // Anim[1] = {} // Anim[1].Texture = "data\\Anim1.png" // Anim[1].Time = 1 // Anim[2] = {} // Anim[2].Texture = "data\\Anim2.png" // Anim[2].Time = 2 // Anim.Mode = "aLOOP" // Anim.AutoStart = true // // data(Anim, "Anim.xml") // // .. will give ... // // <!--Framework Engine Xml database - www.framework-studios.com--> // <fwedata> // <n1.000000 I="N" V="T"> // <Time I="S" V="N">1.000000</Time> // <Texture I="S" V="S">data\Anim1.png</Texture> // </n1.000000> // <n2.000000 I="N" V="T"> // <Time I="S" V="N">2.000000</Time> // <Texture I="S" V="S">data\Anim2.png</Texture> // </n2.000000> // <Mode I="S" V="S">aLOOP</Mode> // <AutoStart I="S" V="B">true</AutoStart> // </fwedata> // // To combine multiple Xml source-files into one table, simply load new // Xml data over an already loaded table. In case a value is indexed by the same // index as in a previous load, the last loaded Xml file will overwrite the value // automagically, erasing double entries. // // For reading a Xml into a LUA table from a stream, read the Xml from the stream, // parse it with TinyXml and use ParseTableFromXml.to parse the Xml data to a table // from memory. // // Example: // TiXmlDocument *XmlDoc = new TiXmlDocument("MyData"); // XmlDoc->Parse((const char *)MyXmlRawData); // TiXmlElement *Root = XmlDoc->FirstChildElement("fwedata"); // ReadTableFromXml(VM, Root, XmlDoc); // lua_setglobal(VM, "MyTableName"); // delete XmlDoc; // actually compiling the framework engine? #ifdef FRAMEWORKENGINE #include "data_xml.h" #else // Include tinyxml.h and LUA here... // // Declare: // // extern int data(lua_State *VM); // // .. and register it to the LUA virtual machine. // // Declare for parsing a Xml Doc from stream to a LUA table: // // void ParseTableFromXml(lua_State *VM, TiXmlElement *Parent, TiXmlDocument *XmlDoc); // // maybe also include <string.h> etc. is required. #endif // how many bytes are required for a string representing a (double or float) number...? #define MAXBYTESINASTRINGREPRESENTINGANUMBER 32 // writes a table to an open Xml document static void WriteTableToXml(lua_State *VM, const int Index, const char *Name, TiXmlNode *Parent) { // create new Xml element node TiXmlElement Element(Name); // if this node is a table, store index type and set value to 'T' for table if (lua_istable(VM, -1)) { // save type if (lua_isnumber(VM, -2)) { Element.SetAttribute("I", "N"); } else { Element.SetAttribute("I", "S"); } Element.SetAttribute("V", "T"); } // start table iteration lua_pushnil(VM); // iterate & save all valid values & tables while (lua_next(VM, Index) != 0) { // is the new node a (nested) table? if (lua_istable(VM, -1)) { // insert the table if (lua_isnumber(VM, -2)) { // index is a number (note that 'lua_tostring' converts a number to string *also* on the lua stack!) char buf[MAXBYTESINASTRINGREPRESENTINGANUMBER]; sprintf(buf, "n%f", lua_tonumber(VM, -2)); // extra 'n' to deceive tinyXml WriteTableToXml(VM, -2, buf, &Element); } else { // index is a string WriteTableToXml(VM, -2, lua_tostring(VM, -2), &Element); } } else // this is not a table, maybe a valid value? { TiXmlElement Index(NULL); // the node's index tag, holding two attributes for index- and value- type TiXmlText Value(NULL); // the actual value of the node // get index & value, types & names switch(lua_type(VM, -2)) { case LUA_TSTRING : Index.SetValue(lua_tostring(VM, -2)); Index.SetAttribute("I", "S"); break; case LUA_TNUMBER : char buf[MAXBYTESINASTRINGREPRESENTINGANUMBER]; sprintf(buf, "n%f", lua_tonumber(VM, -2)); // extra 'n' to deceive tinyXml Index.SetValue(buf); Index.SetAttribute("I", "N"); break; default : // to be sure, ignore other types (userdata, boolean or function) even though one cannot index a table by these types... lua_pop(VM, 1); continue; break; } // get & store the type of the value if (lua_isnumber(VM, -1)) { char buf[MAXBYTESINASTRINGREPRESENTINGANUMBER]; sprintf(buf, "%f", lua_tonumber(VM, -1)); Value.SetValue(buf); Index.SetAttribute("V", "N"); } else if (lua_isboolean(VM, -1)) { Value.SetValue(lua_toboolean(VM, -1) != 0 ? "true" : "false"); Index.SetAttribute("V", "B"); } else if (lua_isstring(VM, -1)) { Value.SetValue(lua_tostring(VM, -1)); Index.SetAttribute("V", "S"); } else { // ignore other types lua_pop(VM, 1); continue; } Index.InsertEndChild(Value); Element.InsertEndChild(Index); } lua_pop(VM, 1); // next in table iteration } // insert new element node to parent node Parent->InsertEndChild(Element); } // parses an open & file-read Xml document into a LUA table void ParseTableFromXml(lua_State *VM, TiXmlElement *Parent, TiXmlDocument *XmlDoc) { TiXmlNode *node = Parent->IterateChildren(NULL); while (node) { if (node->FirstChild() != NULL) { if (node->FirstChild()->Type() == TiXmlNode::NodeType::ELEMENT) { // this node is a tabel... if (node->ToElement()->Attribute("I")) { if (node->ToElement()->Attribute("I")[0] == 'N') { // table is indexed by number lua_pushnumber(VM, atof(&node->Value()[1])); // skip a byte to fool tinyXml... } else { // table is indexed by string lua_pushstring(VM, node->Value()); } lua_newtable(VM); ParseTableFromXml(VM, node->ToElement(), XmlDoc); lua_settable(VM, -3); } else { // no index type node = Parent->IterateChildren(node); continue; } } else { // this node is value... // is the index a string or a number? if (node->ToElement()->Attribute("I")) { switch(node->ToElement()->Attribute("I")[0]) { case 'S' : lua_pushstring(VM, node->Value()); break; case 'N' : lua_pushnumber(VM, atof(&node->Value()[1])); // skip a byte to fool tinyXml... break; default : // invalid index type node = Parent->IterateChildren(node); continue; break; } } else { // no index type node = Parent->IterateChildren(node); continue; } // is the value a string, boolean or a number? if (node->ToElement()->Attribute("V")) { switch(node->ToElement()->Attribute("V")[0]) { case 'S' : lua_pushstring(VM, node->FirstChild()->Value()); lua_settable(VM, -3); break; case 'N' : lua_pushnumber(VM, atof(node->FirstChild()->Value())); lua_settable(VM, -3); break; case 'B' : lua_pushboolean(VM, _strcmpi(node->FirstChild()->Value(), "true") == 0); lua_settable(VM, -3); break; default : // if we got here, "V"alue is of invalid type but "I"ndex // has already been pushed onto the stack, so... lua_pop(VM, 1); } } else { // if we got here, "V"alue does not exist but "I"ndex // has already been pushed onto the stack, so... lua_pop(VM, 1); } } } // next node node = Parent->IterateChildren(node); } } // write example: data(t, "data.xml") -> write table 't' to file 'data.xml' // read example: data("data.xml", t) -> read file 'data.xml' to (existing!) table 't' int data(lua_State *VM) { if ((lua_istable(VM, 1)) && (lua_gettop(VM) == 2)) { // write... TiXmlDocument XmlDoc(lua_tostring(VM, 2)); TiXmlComment Comment; Comment.SetValue("Framework Engine Xml database - www.framework-studios.com"); XmlDoc.InsertEndChild(Comment); WriteTableToXml(VM, 1, "fwedata", &XmlDoc); if (!XmlDoc.SaveFile()) { lua_pushboolean(VM, false); } else { lua_pushboolean(VM, true); } } else if (lua_istable(VM, 2)) // assumes 'false' when there is no second parameter... { // read... TiXmlDocument XmlDoc(TiXmlDocument(lua_tostring(VM, 1))); if (!XmlDoc.LoadFile()) { lua_pushboolean(VM, false); } else { // parse the Xml data into the LUA table... TiXmlElement *root = XmlDoc.FirstChildElement("fwedata"); if (root != NULL) { ParseTableFromXml(VM, root, &XmlDoc); } lua_pushboolean(VM, true); } } else { // actually compiling the framework engine? #ifdef FRAMEWORKENGINE fwse_warning("invalid call to 'data' (no table passed or invalid number of parameters passed) - %s", SandBox->FunctionInfo()); #endif lua_pushboolean(VM, false); } // return 'true' or 'false' to LUA return 1; }
Attachment:
data_xml.zip
Description: Zip compressed data