// // Copyright (C) 2014 Christian Schüller // // This Source Code Form is subject to the terms of the Mozilla Public License // v. 2.0. If a copy of the MPL was not distributed with this file, You can // obtain one at http://mozilla.org/MPL/2.0/. #include "serialize_xml.h" namespace igl { template void serialize_xml(const T& obj,const std::string& filename) { serialize_xml(obj,"object",filename,0); } template void serialize_xml(const T& obj,const std::string& objectName,const std::string& filename,bool binary,bool overwrite) { tinyxml2::XMLDocument* doc = new tinyxml2::XMLDocument(); if(overwrite) { // Check if file exists tinyxml2::XMLError error = doc->LoadFile(filename.c_str()); if(error != tinyxml2::XML_NO_ERROR) { doc->Clear(); } } tinyxml2::XMLElement* element = doc->FirstChildElement("serialization"); if(element == NULL) { element = doc->NewElement("serialization"); doc->InsertEndChild(element); } serialize_xml(obj,objectName,doc,element,binary); // Save tinyxml2::XMLError error = doc->SaveFile(filename.c_str()); if(error != tinyxml2::XML_NO_ERROR) { doc->PrintError(); } delete doc; } template void serialize_xml(const T& obj,const std::string& objectName,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,bool binary) { static_assert(detail_xml::is_serializable::value,"'igl::serialize_xml': type is not serializable"); std::string name(objectName); detail_xml::encodeXMLElementName(name); if(binary) { std::vector buffer; serialize(obj,name,buffer); std::string data = detail_xml::base64_encode(reinterpret_cast(buffer.data()),buffer.size()); tinyxml2::XMLElement* child = detail_xml::getElement(doc,element,name); child->SetAttribute("binary",true); detail_xml::serialize(data,doc,element,name); } else { detail_xml::serialize(obj,doc,element,name); } } template void deserialize_xml(T& obj,const std::string& filename) { deserialize_xml(obj,"object",filename); } template void deserialize_xml(T& obj,const std::string& objectName,const std::string& filename) { tinyxml2::XMLDocument* doc = new tinyxml2::XMLDocument(); tinyxml2::XMLError error = doc->LoadFile(filename.c_str()); if(error != tinyxml2::XML_NO_ERROR) { std::cerr << "File not found!" << std::endl; doc->PrintError(); doc = NULL; } else { tinyxml2::XMLElement* element = doc->FirstChildElement("serialization"); if(element == NULL) { std::cerr << "Name of object not found! Initialized with default value." << std::endl; obj = T(); } else { deserialize_xml(obj,objectName,doc,element); } delete doc; } } template inline void deserialize_xml(T& obj,const std::string& objectName,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element) { static_assert(detail::is_serializable::value,"'igl::deserialize_xml': type is not deserializable"); std::string name(objectName); detail_xml::encodeXMLElementName(name); const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); if(child != NULL) { bool isBinary = false; const tinyxml2::XMLAttribute* attr = child->FindAttribute("binary"); if(attr != NULL) { std::string code; detail_xml::deserialize(code,doc,element,name); std::string decoded = detail_xml::base64_decode(code); std::vector buffer; std::copy(decoded.c_str(),decoded.c_str()+decoded.length(),std::back_inserter(buffer)); deserialize(obj,name,buffer); } else { detail_xml::deserialize(obj,doc,element,name); } } } namespace detail_xml { // fundamental types template std::enable_if_t::value> serialize(const T& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) { tinyxml2::XMLElement* child = getElement(doc,element,name.c_str()); child->SetAttribute("val",obj); } template std::enable_if_t::value> deserialize(T& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name) { const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); if(child != NULL) { getAttribute(child->Attribute("val"),obj); } else { obj = T(); } } // std::string void serialize(const std::string& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) { tinyxml2::XMLElement* child = getElement(doc,element,name.c_str()); child->SetAttribute("val",obj.c_str()); } void deserialize(std::string& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name) { const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); if(child != NULL) { getAttribute(child->Attribute("val"),obj); } else { obj = std::string(""); } } // Serializable template std::enable_if_t::value> serialize(const T& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) { // Serialize object implementing Serializable interface const XMLSerializable& object = dynamic_cast(obj); tinyxml2::XMLElement* child = getElement(doc,element,name.c_str()); object.Serialize(doc,child); } template std::enable_if_t::value> deserialize(T& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name) { const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); if(child != NULL) { obj.Deserialize(doc,child); } else { obj = T(); } } // STL containers template void serialize(const std::pair& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) { tinyxml2::XMLElement* pair = getElement(doc,element,name.c_str()); serialize(obj.first,doc,pair,"first"); serialize(obj.second,doc,pair,"second"); } template void deserialize(std::pair& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name) { const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); if(child != NULL) { deserialize(obj.first,doc,child,"first"); deserialize(obj.second,doc,child,"second"); } else { obj.first = T1(); obj.second = T2(); } } template void serialize(const std::vector& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) { tinyxml2::XMLElement* vector = getElement(doc,element,name.c_str()); vector->SetAttribute("size",(unsigned int)obj.size()); std::stringstream num; for(unsigned int i=0;i void deserialize(std::vector& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name) { obj.clear(); const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); if(child != NULL) { unsigned int size = child->UnsignedAttribute("size"); obj.resize(size); std::stringstream num; for(unsigned int i=0;i void serialize(const std::set& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) { tinyxml2::XMLElement* set = getElement(doc,element,name.c_str()); set->SetAttribute("size",(unsigned int)obj.size()); std::stringstream num; typename std::set::iterator iter = obj.begin(); for(int i=0;iter!=obj.end();iter++,i++) { num.str(""); num << "value" << i; serialize((T)*iter,doc,set,num.str()); } } template void deserialize(std::set& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name) { obj.clear(); const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); if(child != NULL) { unsigned int size = child->UnsignedAttribute("size"); std::stringstream num; typename std::set::iterator iter = obj.begin(); for(int i=0;i void serialize(const std::map& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) { tinyxml2::XMLElement* map = getElement(doc,element,name.c_str()); map->SetAttribute("size",(unsigned int)obj.size()); std::stringstream num; typename std::map::const_iterator iter = obj.cbegin(); for(int i=0;iter!=obj.end();iter++,i++) { num.str(""); num << "value" << i; serialize((std::pair)*iter,doc,map,num.str()); } } template void deserialize(std::map& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name) { obj.clear(); const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); if(child != NULL) { unsigned int size = child->UnsignedAttribute("size"); std::stringstream num; typename std::map::iterator iter = obj.begin(); for(int i=0;i pair; deserialize(pair,doc,child,num.str()); obj.insert(pair); } } else { obj.clear(); } } // Eigen types template void serialize(const Eigen::Matrix& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) { tinyxml2::XMLElement* matrix = getElement(doc,element,name.c_str()); const unsigned int rows = obj.rows(); const unsigned int cols = obj.cols(); matrix->SetAttribute("rows",rows); matrix->SetAttribute("cols",cols); char buffer[200]; std::stringstream ms; ms << "\n"; for(unsigned int r=0;r 1) mString[mString.size()-2] = '\0'; matrix->SetAttribute("matrix",mString.c_str()); } template void deserialize(Eigen::Matrix& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name) { const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); bool initialized = false; if(child != NULL) { const unsigned int rows = child->UnsignedAttribute("rows"); const unsigned int cols = child->UnsignedAttribute("cols"); if(rows > 0 && cols > 0) { obj.resize(rows,cols); const tinyxml2::XMLAttribute* attribute = child->FindAttribute("matrix"); if(attribute != NULL) { std::string matTemp; getAttribute(attribute->Value(),matTemp); std::string line,srows,scols; std::stringstream mats; mats << matTemp; int r=0; std::string val; // for each line getline(mats,line); while(getline(mats,line)) { // get current line std::stringstream liness(line); for(unsigned int c=0;c(); } } template void serialize(const Eigen::SparseMatrix& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) { tinyxml2::XMLElement* matrix = getElement(doc,element,name.c_str()); const unsigned int rows = obj.rows(); const unsigned int cols = obj.cols(); matrix->SetAttribute("rows",rows); matrix->SetAttribute("cols",cols); char buffer[200]; std::stringstream ms; ms << "\n"; for(int k=0;k::InnerIterator it(obj,k);it;++it) { tinyxml2::XMLUtil::ToStr(it.value(),buffer,200); ms << it.row() << "," << it.col() << "," << buffer << "\n"; } } std::string mString = ms.str(); if(mString.size() > 0) mString[mString.size()-1] = '\0'; matrix->SetAttribute("matrix",mString.c_str()); } template void deserialize(Eigen::SparseMatrix& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name) { const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); bool initialized = false; if(child != NULL) { const unsigned int rows = child->UnsignedAttribute("rows"); const unsigned int cols = child->UnsignedAttribute("cols"); if(rows > 0 && cols > 0) { obj.resize(rows,cols); obj.setZero(); const tinyxml2::XMLAttribute* attribute = child->FindAttribute("matrix"); if(attribute != NULL) { std::string matTemp; getAttribute(attribute->Value(),matTemp); std::string line,srows,scols; std::stringstream mats; mats << matTemp; std::vector > triplets; int r=0; std::string val; // for each line getline(mats,line); while(getline(mats,line)) { // get current line std::stringstream liness(line); // row getline(liness,val,','); int row = atoi(val.c_str()); // col getline(liness,val,','); int col = atoi(val.c_str()); // val getline(liness,val); T value; getAttribute(val.c_str(),value); triplets.push_back(Eigen::Triplet(row,col,value)); r++; } obj.setFromTriplets(triplets.begin(),triplets.end()); initialized = true; } } } if(!initialized) { obj = Eigen::SparseMatrix(); } } // pointers template std::enable_if_t::value> serialize(const T& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) { tinyxml2::XMLElement* pointer = getElement(doc,element,name.c_str()); bool isNullPtr = (obj == NULL); pointer->SetAttribute("isNullPtr",isNullPtr); if(isNullPtr == false) detail_xml::serialize(*obj,doc,element,name); } template std::enable_if_t::value> deserialize(T& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name) { const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); if(child != NULL) { bool isNullPtr = child->BoolAttribute("isNullPtr"); if(isNullPtr) { if(obj != NULL) { std::cout << "deserialization: possible memory leak for '" << typeid(obj).name() << "'" << std::endl; obj = NULL; } } else { if(obj != NULL) std::cout << "deserialization: possible memory leak for '" << typeid(obj).name() << "'" << std::endl; obj = new std::remove_pointer::type(); detail_xml::deserialize(*obj,doc,element,name); } } } // helper functions tinyxml2::XMLElement* getElement(tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) { tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); if(child == NULL) { child = doc->NewElement(name.c_str()); element->InsertEndChild(child); } return child; } void getAttribute(const char* src,bool& dest) { tinyxml2::XMLUtil::ToBool(src,&dest); } void getAttribute(const char* src,char& dest) { dest = (char)atoi(src); } void getAttribute(const char* src,std::string& dest) { dest = src; } void getAttribute(const char* src,float& dest) { tinyxml2::XMLUtil::ToFloat(src,&dest); } void getAttribute(const char* src,double& dest) { tinyxml2::XMLUtil::ToDouble(src,&dest); } template std::enable_if_t::value && std::is_unsigned::value> getAttribute(const char* src,T& dest) { unsigned int val; tinyxml2::XMLUtil::ToUnsigned(src,&val); dest = (T)val; } template std::enable_if_t::value && !std::is_unsigned::value> getAttribute(const char* src,T& dest) { int val; tinyxml2::XMLUtil::ToInt(src,&val); dest = (T)val; } // tinyXML2 related stuff static const int numForbiddenChars = 8; static const char forbiddenChars[] ={' ','/','~','#','&','>','<','='}; void replaceSubString(std::string& str,const std::string& search,const std::string& replace) { size_t pos = 0; while((pos = str.find(search,pos)) != std::string::npos) { str.replace(pos,search.length(),replace); pos += replace.length(); } } void encodeXMLElementName(std::string& name) { // must not start with a digit if(isdigit(*name.begin())) { name = ":::" + name; } std::stringstream stream; for(int i=0;i> 2; char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for(i = 0; (i <4) ; i++) ret += base64_chars[char_array_4[i]]; i = 0; } } if(i) { for(j = i; j < 3; j++) char_array_3[j] = '\0'; char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for(j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]]; while((i++ < 3)) ret += '='; } return ret; } std::string base64_decode(std::string const& encoded_string) { int in_len = encoded_string.size(); int i = 0; int j = 0; int in_ = 0; unsigned char char_array_4[4],char_array_3[3]; std::string ret; // construct fast lookup table // added by Christian Schüller (schuellc@inf.ethz.ch) int charLookup[200]; for(int i=0;i> 4); char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; for(i = 0; (i < 3); i++) ret += char_array_3[i]; i = 0; } } if(i) { for(j = i; j <4; j++) char_array_4[j] = 0; for(j = 0; j <4; j++) char_array_4[j] = base64_chars.find(char_array_4[j]); char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; for(j = 0; (j < i - 1); j++) ret += char_array_3[j]; } return ret; } } }