#ifndef s11n_EXPAT_SERIALIZER_HPP_INCLUDED #define s11n_EXPAT_SERIALIZER_HPP_INCLUDED 1 #include // translate() #include // COUT/CERR #include #include // node_traits #include #define MAGIC_COOKIE_EXPAT_XML "" #include #include #define EXPATDEBUG if(0) CERR #define EXPAT_CLASS_ATTRIBUTE "class" namespace s11n { namespace io { namespace sharing { /** Sharing context used by expat_serializer. */ struct expat_sharing_context {}; } /** convenience typedef */ typedef std::map entity_translation_map; /** The entity translations map used by expat_serializer. */ entity_translation_map & expat_serializer_translations(); /** expat_serializer is an XML-based Serializer, using libexpat to read it's data. */ template class expat_serializer : public data_node_serializer { public: typedef NodeType node_type; /** Performs a "fast" check for XML key validity on s: if the first char is alpha or underscore, the function returns true, else it returns false. */ static bool is_valid_xml_key( const std::string & s ) { return ( (!s.empty()) && ( std::isalpha(s[0]) || ('_' == s[0]) ) ); } typedef expat_serializer this_type; // convenience typedef expat_serializer() : m_depth(0) { this->magic_cookie( MAGIC_COOKIE_EXPAT_XML ); } virtual ~expat_serializer() {} /** Writes src out to dest. */ virtual bool serialize( const node_type & src, std::ostream & dest ) { typedef ::s11n::node_traits NT; // INDENT() is a helper macro for some serializers. #define INDENT(LEVEL,ECHO) indent = ""; for( size_t i = 0; i < depth + LEVEL; i++ ) { indent += '\t'; if(ECHO) buff << '\t'; } std::ostringstream buff; // we buffer so we can handle self-closing nodes size_t depth = this->m_depth++; if ( 0 == depth ) { buff << this->magic_cookie() << '\n'; } std::string nname = NT::name(src); if( ! is_valid_xml_key( nname ) ) { std::ostringstream err; err << "expat_serializer::serialize(): node name '" << nname << "' is not a valid XML tag name."; throw s11n::io_exception( err.str(), __FILE__, __LINE__ ); } std::string impl = NT::class_name(src); ::s11n::io::strtool::translate_entities( impl, expat_serializer_translations(), false ); std::string indent; buff << "<" << nname << " "<\n"; INDENT(1,0); for ( ; cet != cit; ++cit ) { key = ( *cit ).first; if( ! is_valid_xml_key( key ) ) { std::ostringstream err; err << "expat_serializer::serialize(): key '" << key << "' is not a valid XML tag name."; throw s11n::io_exception( err.str(), __FILE__, __LINE__ ); } propval = ( *cit ).second; buff << indent << "<" << key; if( propval.empty() ) { buff << "/>"; } else { buff << ">"; ::s11n::io::strtool::translate_entities( propval, expat_serializer_translations(), false ); buff << propval; buff << ""; } buff << "\n"; } } typedef typename NT::child_list_type CHLT; typename CHLT::const_iterator chit = NT::children(src).begin(), chet = NT::children(src).end(); if( chet != chit ) { // got kids? if( !closed ) { // close node opener buff << ">\n"; closed = true; } INDENT(1,0); std::for_each( chit, chet, node_child_simple_formatter( *this, buff, indent, "" ) ); } if( closed ) { INDENT(0,1); buff << ""; } else // self-close node { buff << "/>"; } dest << buff.str() << "\n"; if( 0 == depth ) { dest.flush(); // if we don't do this then the client is possibly forced to flush() the stream :/ } --this->m_depth; return true; #undef INDENT } static void XMLCALL start_node( void *, const char * name, const char ** attr ) { m_cbuf = ""; if( attr[0] ) { // object node std::string clname = "WTF?"; const std::string classnameattr = std::string(EXPAT_CLASS_ATTRIBUTE); for( int i = 0; attr[i]; i += 2 ) { if( attr[i] == classnameattr ) { clname = attr[i+1]; break; } } EXPATDEBUG << "Opening object node["<expat_parse_stream( src ); } private: size_t m_depth; static data_node_tree_builder m_builder; static std::string m_name; // current node's name static std::string m_cbuf; // cdata buffer }; template data_node_tree_builder expat_serializer::m_builder; template std::string expat_serializer::m_name; template std::string expat_serializer::m_cbuf; } // namespace io } // namespace s11n #undef EXPATDEBUG #undef EXPAT_CLASS_ATTRIBUTE #endif // s11n_EXPAT_SERIALIZER_HPP_INCLUDED