// This file is part of libigl, a simple c++ geometry processing library. // // 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" #include "../STR.h" #include "../serialize.h" #include "XMLSerializable.h" #include #include #include namespace igl { namespace xml { template IGL_INLINE void serialize_xml( const T& obj, const std::string& filename) { serialize_xml(obj,"object",filename,false,true); } template IGL_INLINE 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 == false) { // Check if file exists tinyxml2::XMLError error = doc->LoadFile(filename.c_str()); if(error != tinyxml2::XML_SUCCESS) { 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_SUCCESS) { doc->PrintError(); } delete doc; } template IGL_INLINE void serialize_xml( const T& obj, const std::string& objectName, tinyxml2::XMLDocument* doc, tinyxml2::XMLElement* element, bool binary) { static_assert( serialization_xml::is_serializable::value, "'igl::xml::serialize_xml': type is not serializable"); std::string name(objectName); serialization_xml::encodeXMLElementName(name); tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str()); if(child != NULL) element->DeleteChild(child); child = doc->NewElement(name.c_str()); element->InsertEndChild(child); if(binary) { std::vector buffer; serialize(obj,name,buffer); std::string data = serialization_xml::base64_encode( reinterpret_cast( buffer.data()),buffer.size()); child->SetAttribute("binary",true); serialization_xml::serialize(data,doc,element,name); } else { serialization_xml::serialize(obj,doc,element,name); } } template IGL_INLINE void deserialize_xml(T& obj,const std::string& filename) { deserialize_xml(obj,"object",filename); } template IGL_INLINE 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_SUCCESS) { std::cerr << "File not found!" << std::endl; doc->PrintError(); delete doc; } 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 IGL_INLINE void deserialize_xml(T& obj,const std::string& objectName,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element) { static_assert(serialization::is_serializable::value,"'igl::xml::deserialize_xml': type is not deserializable"); std::string name(objectName); serialization_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; serialization_xml::deserialize(code,doc,element,name); std::string decoded = serialization_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 { serialization_xml::deserialize(obj,doc,element,name); } } } namespace serialization_xml { // fundamental types template IGL_INLINE typename std::enable_if::value>::type 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 IGL_INLINE typename std::enable_if::value>::type 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 IGL_INLINE 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()); } IGL_INLINE 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 IGL_INLINE typename std::enable_if::value>::type serialize(const T& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name) { // Serialize object implementing Serializable interface const XMLSerializableBase& object = dynamic_cast(obj); tinyxml2::XMLElement* child = getElement(doc,element,name.c_str()); object.Serialize(doc,child); } template IGL_INLINE typename std::enable_if::value>::type 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 IGL_INLINE 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 IGL_INLINE 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 IGL_INLINE 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 IGL_INLINE 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 IGL_INLINE 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 IGL_INLINE 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 IGL_INLINE 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 IGL_INLINE 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(); } } template IGL_INLINE void serialize( const Eigen::Matrix& obj, const std::string& name, const std::function& to_string, tinyxml2::XMLDocument* doc, tinyxml2::XMLElement* element) { 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); std::stringstream ms; ms << "\n"; for(unsigned int r=0;r 1) mString[mString.size()-2] = '\0'; matrix->SetAttribute("matrix",mString.c_str()); } // Eigen types template IGL_INLINE void serialize( const Eigen::Matrix& obj, tinyxml2::XMLDocument* doc, tinyxml2::XMLElement* element, const std::string& name) { const std::function to_string = [](const T & v)->std::string { return STR(std::setprecision(std::numeric_limits::digits10+2)< IGL_INLINE void deserialize( const tinyxml2::XMLDocument* doc, const tinyxml2::XMLElement* element, const std::string& name, const std::function & from_string, Eigen::Matrix& obj) { 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 IGL_INLINE void deserialize( Eigen::Matrix& obj, const tinyxml2::XMLDocument* doc, const tinyxml2::XMLElement* element, const std::string& name) { const std::function & from_string = [](const std::string & s,T & v) { getAttribute(s.c_str(),v); }; deserialize(doc,element,name,from_string,obj); } template IGL_INLINE 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 IGL_INLINE 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 IGL_INLINE typename std::enable_if::value>::type 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) serialization_xml::serialize(*obj,doc,element,name); } template IGL_INLINE typename std::enable_if::value>::type 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 typename std::remove_pointer::type(); serialization_xml::deserialize(*obj,doc,element,name); } } } // helper functions IGL_INLINE 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; } IGL_INLINE void getAttribute(const char* src,bool& dest) { tinyxml2::XMLUtil::ToBool(src,&dest); } IGL_INLINE void getAttribute(const char* src,char& dest) { dest = (char)atoi(src); } IGL_INLINE void getAttribute(const char* src,std::string& dest) { dest = src; } IGL_INLINE void getAttribute(const char* src,float& dest) { tinyxml2::XMLUtil::ToFloat(src,&dest); } IGL_INLINE void getAttribute(const char* src,double& dest) { tinyxml2::XMLUtil::ToDouble(src,&dest); } template IGL_INLINE typename std::enable_if::value && std::is_unsigned::value>::type getAttribute(const char* src,T& dest) { unsigned int val; tinyxml2::XMLUtil::ToUnsigned(src,&val); dest = (T)val; } template IGL_INLINE typename std::enable_if::value && !std::is_unsigned::value>::type 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[] ={' ','/','~','#','&','>','<','='}; IGL_INLINE 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(); } } IGL_INLINE 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<(int)(base64_chars.length());i++) charLookup[(int)base64_chars[i]] = i; while(in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { char_array_4[i++] = encoded_string[in_]; in_++; if(i ==4) { for(i = 0; i <4; i++) char_array_4[i] = charLookup[char_array_4[i]]; // new fast lookup //char_array_4[i] = base64_chars.find(char_array_4[i]); // original version 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(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; } } } } #ifdef IGL_STATIC_LIBRARY // Explicit template instantiation template void igl::xml::serialize_xml > >(std::vector > const&, std::basic_string, std::allocator > const&, std::basic_string, std::allocator > const&, bool, bool); template void igl::xml::deserialize_xml > >(std::vector >&, std::basic_string, std::allocator > const&, std::basic_string, std::allocator > const&); #endif