Add base/Json.h: JSON encoder for e.g. sending data to plugins
authorMichael Andres <ma@suse.de>
Wed, 20 Nov 2013 15:21:07 +0000 (16:21 +0100)
committerMichael Andres <ma@suse.de>
Wed, 20 Nov 2013 15:23:38 +0000 (16:23 +0100)
zypp/CMakeLists.txt
zypp/base/Json.h [new file with mode: 0644]

index 741ef8dfe627f134c0bcbaa5c0a59ee7a2e63cb6..671af88de8416bcf41bc5af97b3749d755de60f1 100644 (file)
@@ -239,6 +239,7 @@ SET( zypp_base_HEADERS
   base/IOStream.h
   base/InputStream.h
   base/Iterator.h
+  base/Json.h
   base/LogControl.h
   base/LogTools.h
   base/Logger.h
diff --git a/zypp/base/Json.h b/zypp/base/Json.h
new file mode 100644 (file)
index 0000000..7957af9
--- /dev/null
@@ -0,0 +1,383 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/base/Json.h
+ *
+*/
+#ifndef ZYPP_BASE_JSON_H
+#define ZYPP_BASE_JSON_H
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+#include <list>
+#include <set>
+#include <map>
+
+#include "zypp/base/Easy.h"
+#include "zypp/base/String.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{
+  ///////////////////////////////////////////////////////////////////
+  namespace json
+  {
+    // JSN Keywords
+    inline static const std::string & nullJSON()       { static const std::string _s( "null" );  return _s; }
+    inline static const std::string & trueJSON()       { static const std::string _s( "true" );  return _s; }
+    inline static const std::string & falseJSON()      { static const std::string _s( "false" ); return _s; }
+
+    ///////////////////////////////////////////////////////////////////
+    namespace detail
+    {
+      inline std::string strEncode( std::string val_r )
+      {
+       typedef unsigned char uchar;
+
+       std::string::size_type add = 2; // enclosing "s
+       for_( r, val_r.begin(), val_r.end() )
+       {
+         if ( uchar(*r) < 32u )
+         {
+           switch ( *r )
+           {
+             case '\b':
+             case '\f':
+             case '\n':
+             case '\r':
+             case '\t':
+               add += 1;       // "\c"
+               break;
+             default:
+               add += 5;       // "\uXXXX"
+               break;
+           }
+         }
+         else
+         {
+           switch ( *r )
+           {
+             case '"':
+             case '/':
+             case '\\':
+               add += 1;       // \-escape
+               break;
+           }
+         }
+       }
+
+       val_r.resize( val_r.size() + add, '@' );
+       auto w( val_r.rbegin() );
+       auto r( w + add );
+
+       *w++ = '"';
+       for ( ; r != val_r.rend(); ++r )
+       {
+         if ( uchar(*r) < 32u )
+         {
+           static const char * digit = "0123456789abcdef";
+           switch ( *r )
+           {
+             case '\b':        // "\c"
+               *w++ = 'b';
+               *w++ = '\\';
+               break;
+             case '\f':        // "\c"
+               *w++ = 'f';
+               *w++ = '\\';
+               break;
+             case '\n':        // "\c"
+               *w++ = 'n';
+               *w++ = '\\';
+               break;
+             case '\r':        // "\c"
+               *w++ = 'r';
+               *w++ = '\\';
+               break;
+             case '\t':        // "\c"
+               *w++ = 't';
+               *w++ = '\\';
+               break;
+             default:          // "\uXXXX"
+               *w++ = digit[uchar(*r) % 15];
+               *w++ = digit[uchar(*r) / 16];
+               *w++ = '0';
+               *w++ = '0';
+               *w++ = 'u';
+               *w++ = '\\';
+               break;
+           }
+         }
+         else
+         {
+           switch ( (*w++ = *r) )
+           {
+             case '"':
+             case '/':
+             case '\\':        // \-escape
+               *w++ = '\\';
+               break;
+           }
+         }
+       }
+       *w++ = '"';
+       return val_r;
+      }
+    } // namespace detail
+    ///////////////////////////////////////////////////////////////////
+
+    // null
+    inline std::string toJSON( void )                  { return nullJSON(); }
+    inline std::string toJSON( std::nullptr_t )                { return nullJSON(); }
+
+    // bool
+    inline std::string toJSON( bool val_r  )           { return val_r ? trueJSON() : falseJSON(); }
+    inline std::string toJSON( const void * val_r )    { return val_r ? trueJSON() : falseJSON(); }
+
+    // numbers
+    inline std::string toJSON( short val_r )           { return str::numstring( val_r ); }
+    inline std::string toJSON( unsigned short val_r )  { return str::numstring( val_r ); }
+    inline std::string toJSON( int val_r )             { return str::numstring( val_r ); }
+    inline std::string toJSON( unsigned val_r )                { return str::numstring( val_r ); }
+    inline std::string toJSON( long val_r )            { return str::numstring( val_r ); }
+    inline std::string toJSON( unsigned long val_r )   { return str::numstring( val_r ); }
+    inline std::string toJSON( long long val_r )       { return str::numstring( val_r ); }
+    inline std::string toJSON( unsigned long long val_r ){ return str::numstring( val_r ); }
+
+    // strings
+    inline std::string toJSON( const char val_r )      { return detail::strEncode( std::string( 1, val_r ) ); }
+    inline std::string toJSON( const char * val_r )    { return val_r ? detail::strEncode( val_r ) : nullJSON(); }
+    inline std::string toJSON( const std::string & val_r ){ return detail::strEncode( val_r ); }
+
+    // container to Array
+    template <class V> std::string toJSON( const std::vector<V> & cont_r );
+    template <class V> std::string toJSON( const std::list<V> & cont_r );
+    template <class V> std::string toJSON( const std::set<V> & cont_r );
+
+    // map to Object
+    template <class K, class V> std::string toJSON( const std::map<K,V> & cont_r );
+
+    /** Type to JSON string representation.
+     * This can be implemented as non-static memberfunction \c asJSON,
+     * or as non-memberfunction \c toJSON;
+     * \code
+     *   class Type;
+     *   std::string Type::asJSON() const;
+     *   std::string toJSON( const Type & );
+     * \endcode
+     */
+    template <class T>
+    std::string toJSON( const T & val_r ) { return val_r.asJSON(); }
+
+    ///////////////////////////////////////////////////////////////////
+    /// \class Value
+    /// \brief JSON representation of datatypes via \ref toJSON
+    /// \code
+    ///   namespace mynamspace
+    ///   {
+    ///     struct Mydata
+    ///     {...};
+    ///
+    ///     std::string toJSON( const Mydata & )
+    ///     { return json::Array{ "answer", 42 }.asJSON(); }
+    ///   }
+    ///
+    ///   mynamspace::Mydata data;
+    ///   json::Object bigone {
+    ///     { "mydata",  data },
+    ///     { "panic",   false },
+    ///     { "nested",  json::Object{ {"one",1}, {"two",2}, {"three",3} } }
+    ///   };
+    ///
+    ///   cout << bigone << endl;
+    ///\endcode
+    /// \see http://www.json.org/
+    ///////////////////////////////////////////////////////////////////
+    struct Value
+    {
+      /** Default ctor (null) */
+      Value() : _data( toJSON() ) {}
+
+      /** Copy ctor */
+      Value( const Value & rhs ) : _data( rhs._data ) {}
+
+      /** Ctor creating a JSON representation of \a T via \ref toJSON(T) */
+      template <class T>
+      Value( const T & val_r ) : _data( toJSON( val_r ) ) {}
+
+      /** JSON representation */
+      const std::string & asJSON() const
+      { return _data; }
+
+      /** String representation */
+      const std::string & asString() const
+      { return asJSON(); }
+
+      /** Stream output */
+      std::ostream & dumpOn( std::ostream & str ) const
+      { return str << _data; }
+
+    private:
+      std::string _data;
+    };
+
+    /** \relates Value Stream output */
+    inline std::ostream & operator<<( std::ostream & str, const Value & obj )
+    { return obj.dumpOn( str ); }
+
+    ///////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    /// \class String
+    /// \brief JSON string
+    /// Force representation as JSON string, mapping e.g. \c null values
+    /// to an empty string. Maninly used in \ref Object as key.
+    ///////////////////////////////////////////////////////////////////
+    struct String : public Value
+    {
+      String()                         : Value( "" ) {}
+      String( std::nullptr_t )         : Value( "" ) {}
+
+      String( const char val_r )       : Value( val_r ) {}
+      String( const char * val_r )     : Value( val_r ? val_r : "" ) {}
+      String( const std::string & val_r ): Value( val_r ) {}
+    };
+
+    ///////////////////////////////////////////////////////////////////
+    /// \class Array
+    /// \brief JSON array
+    ///////////////////////////////////////////////////////////////////
+    struct Array
+    {
+      Array() {}
+
+      /** Construct from container iterator */
+      template <class Iterator>
+      Array( Iterator begin, Iterator end )
+      { for_( it, begin, end ) add( *it ); }
+
+      /** Construct from container initializer list { v1, v2,... } */
+      Array( const std::initializer_list<Value> & contents_r )
+       : Array( contents_r.begin(), contents_r.end() )
+      {}
+
+      /** Push JSON Value to Array */
+      void add( const Value & val_r )
+      { _data.push_back( val_r.asJSON() ); }
+
+      /** \overload from container initializer list { v1, v2,... } */
+      void add( const std::initializer_list<Value> & contents_r )
+      { for_( it, contents_r.begin(), contents_r.end() ) add( *it ); }
+
+      /** JSON representation */
+      std::string asJSON() const
+      { return str::Str() << *this; }
+
+      /** String representation */
+      std::string asString() const
+      { return asJSON(); }
+
+      /** Stream output */
+      std::ostream & dumpOn( std::ostream & str ) const
+      {
+       if ( _data.empty() )
+         return str << "[]";
+       str << '[' << *_data.begin();
+       for_( val, ++_data.begin(), _data.end() )
+         str << ", " << *val;
+       return str << ']';
+      }
+
+    private:
+      std::list<std::string> _data;
+    };
+
+    /** \relates Array Stream output */
+    inline std::ostream & operator<<( std::ostream & str, const Array & obj )
+    { return obj.dumpOn( str ); }
+
+    template <class V>
+    std::string toJSON( const std::vector<V> & cont_r )
+    { return json::Array( cont_r.begin(), cont_r.end() ).asJSON(); }
+
+    template <class V>
+    std::string toJSON( const std::list<V> & cont_r )
+    { return json::Array( cont_r.begin(), cont_r.end() ).asJSON(); }
+
+    template <class V>
+    std::string toJSON( const std::set<V> & cont_r )
+    { return json::Array( cont_r.begin(), cont_r.end() ).asJSON(); }
+
+    ///////////////////////////////////////////////////////////////////
+    /// \class Object
+    /// \brief JSON object
+    ///////////////////////////////////////////////////////////////////
+    struct Object
+    {
+      Object() {}
+
+      /** Construct from map-iterator */
+      template <class Iterator>
+      Object( Iterator begin, Iterator end )
+      { for_( it, begin, end ) add( it->first, it->second ); }
+
+      /** Construct from map-initializer list { {k1,v1}, {k2,v2},... } */
+      Object( const std::initializer_list<std::pair<String, Value>> & contents_r )
+       : Object( contents_r.begin(), contents_r.end() )
+      {}
+
+      /** Add key/value pair */
+      void add( const String & key_r, const Value & val_r )
+      { _data[key_r.asJSON()] = val_r.asJSON(); }
+
+      /** \overload from map-initializer list { {k1,v1}, {k2,v2},... } */
+      void add( const std::initializer_list<std::pair<String, Value>> & contents_r )
+      { for_( it, contents_r.begin(), contents_r.end() ) add( it->first, it->second ); }
+
+      /** JSON representation */
+      std::string asJSON() const
+      { return str::Str() << *this; }
+
+      /** String representation */
+      std::string asString() const
+      { return asJSON(); }
+
+      /** Stream output */
+      std::ostream & dumpOn( std::ostream & str ) const
+      {
+       using std::endl;
+       if ( _data.empty() )
+         return str << "{}";
+       dumpOn( str << '{' << endl, _data.begin() );
+       for_ ( val, ++_data.begin(), _data.end() )
+         dumpOn( str << ',' << endl, val );
+       return str << endl << '}';
+      }
+
+    private:
+      std::ostream & dumpOn( std::ostream & str, std::map<std::string,std::string>::const_iterator val_r ) const
+      { return str << val_r->first << ": " << val_r->second; }
+
+      std::map<std::string,std::string> _data;
+    };
+
+    /** \relates Object Stream output */
+    inline std::ostream & operator<<( std::ostream & str, const Object & obj )
+    { return obj.dumpOn( str ); }
+
+    template <class K, class V>
+    std::string toJSON( const std::map<K,V> & cont_r )
+    { return json::Object( cont_r.begin(), cont_r.end() ).asJSON(); }
+
+
+  } // namespace json
+  ///////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_JSON_H