String: don't split at quoted sep-chars
[platform/upstream/libzypp.git] / zypp / base / Json.h
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/base/Json.h
10  *
11 */
12 #ifndef ZYPP_BASE_JSON_H
13 #define ZYPP_BASE_JSON_H
14
15 #include <iosfwd>
16 #include <string>
17 #include <vector>
18 #include <list>
19 #include <set>
20 #include <map>
21
22 #include "zypp/base/Easy.h"
23 #include "zypp/base/String.h"
24
25 ///////////////////////////////////////////////////////////////////
26 namespace zypp
27 {
28   ///////////////////////////////////////////////////////////////////
29   namespace json
30   {
31     // JSN Keywords
32     inline static const std::string & nullJSON()        { static const std::string _s( "null" );  return _s; }
33     inline static const std::string & trueJSON()        { static const std::string _s( "true" );  return _s; }
34     inline static const std::string & falseJSON()       { static const std::string _s( "false" ); return _s; }
35
36     ///////////////////////////////////////////////////////////////////
37     namespace detail
38     {
39       inline std::string strEncode( std::string val_r )
40       {
41         typedef unsigned char uchar;
42
43         std::string::size_type add = 2; // enclosing "s
44         for_( r, val_r.begin(), val_r.end() )
45         {
46           if ( uchar(*r) < 32u )
47           {
48             switch ( *r )
49             {
50               case '\b':
51               case '\f':
52               case '\n':
53               case '\r':
54               case '\t':
55                 add += 1;       // "\c"
56                 break;
57               default:
58                 add += 5;       // "\uXXXX"
59                 break;
60             }
61           }
62           else
63           {
64             switch ( *r )
65             {
66               case '"':
67               case '/':
68               case '\\':
69                 add += 1;       // \-escape
70                 break;
71             }
72           }
73         }
74
75         val_r.resize( val_r.size() + add, '@' );
76         auto w( val_r.rbegin() );
77         auto r( w + add );
78
79         *w++ = '"';
80         for ( ; r != val_r.rend(); ++r )
81         {
82           if ( uchar(*r) < 32u )
83           {
84             static const char * digit = "0123456789abcdef";
85             switch ( *r )
86             {
87               case '\b':        // "\c"
88                 *w++ = 'b';
89                 *w++ = '\\';
90                 break;
91               case '\f':        // "\c"
92                 *w++ = 'f';
93                 *w++ = '\\';
94                 break;
95               case '\n':        // "\c"
96                 *w++ = 'n';
97                 *w++ = '\\';
98                 break;
99               case '\r':        // "\c"
100                 *w++ = 'r';
101                 *w++ = '\\';
102                 break;
103               case '\t':        // "\c"
104                 *w++ = 't';
105                 *w++ = '\\';
106                 break;
107               default:          // "\uXXXX"
108                 *w++ = digit[uchar(*r) % 15];
109                 *w++ = digit[uchar(*r) / 16];
110                 *w++ = '0';
111                 *w++ = '0';
112                 *w++ = 'u';
113                 *w++ = '\\';
114                 break;
115             }
116           }
117           else
118           {
119             switch ( (*w++ = *r) )
120             {
121               case '"':
122               case '/':
123               case '\\':        // \-escape
124                 *w++ = '\\';
125                 break;
126             }
127           }
128         }
129         *w++ = '"';
130         return val_r;
131       }
132     } // namespace detail
133     ///////////////////////////////////////////////////////////////////
134
135     // null
136     inline std::string toJSON( void )                   { return nullJSON(); }
137     inline std::string toJSON( std::nullptr_t )         { return nullJSON(); }
138
139     // bool
140     inline std::string toJSON( bool val_r  )            { return val_r ? trueJSON() : falseJSON(); }
141     inline std::string toJSON( const void * val_r )     { return val_r ? trueJSON() : falseJSON(); }
142
143     // numbers
144     inline std::string toJSON( short val_r )            { return str::numstring( val_r ); }
145     inline std::string toJSON( unsigned short val_r )   { return str::numstring( val_r ); }
146     inline std::string toJSON( int val_r )              { return str::numstring( val_r ); }
147     inline std::string toJSON( unsigned val_r )         { return str::numstring( val_r ); }
148     inline std::string toJSON( long val_r )             { return str::numstring( val_r ); }
149     inline std::string toJSON( unsigned long val_r )    { return str::numstring( val_r ); }
150     inline std::string toJSON( long long val_r )        { return str::numstring( val_r ); }
151     inline std::string toJSON( unsigned long long val_r ){ return str::numstring( val_r ); }
152
153     // strings
154     inline std::string toJSON( const char val_r )       { return detail::strEncode( std::string( 1, val_r ) ); }
155     inline std::string toJSON( const char * val_r )     { return val_r ? detail::strEncode( val_r ) : nullJSON(); }
156     inline std::string toJSON( const std::string & val_r ){ return detail::strEncode( val_r ); }
157
158     // container to Array
159     template <class V> std::string toJSON( const std::vector<V> & cont_r );
160     template <class V> std::string toJSON( const std::list<V> & cont_r );
161     template <class V> std::string toJSON( const std::set<V> & cont_r );
162
163     // map to Object
164     template <class K, class V> std::string toJSON( const std::map<K,V> & cont_r );
165
166     /** Type to JSON string representation.
167      * This can be implemented as non-static memberfunction \c asJSON,
168      * or as non-memberfunction \c toJSON;
169      * \code
170      *   class Type;
171      *   std::string Type::asJSON() const;
172      *   std::string toJSON( const Type & );
173      * \endcode
174      */
175     template <class T>
176     std::string toJSON( const T & val_r ) { return val_r.asJSON(); }
177
178     ///////////////////////////////////////////////////////////////////
179     /// \class Value
180     /// \brief JSON representation of datatypes via \ref toJSON
181     /// \code
182     ///   namespace mynamspace
183     ///   {
184     ///     struct Mydata
185     ///     {...};
186     ///
187     ///     std::string toJSON( const Mydata & )
188     ///     { return json::Array{ "answer", 42 }.asJSON(); }
189     ///   }
190     ///
191     ///   mynamspace::Mydata data;
192     ///   json::Object bigone {
193     ///     { "mydata",  data },
194     ///     { "panic",   false },
195     ///     { "nested",  json::Object{ {"one",1}, {"two",2}, {"three",3} } }
196     ///   };
197     ///
198     ///   cout << bigone << endl;
199     ///\endcode
200     /// \see http://www.json.org/
201     ///////////////////////////////////////////////////////////////////
202     struct Value
203     {
204       /** Default ctor (null) */
205       Value() : _data( toJSON() ) {}
206
207       /** Copy ctor */
208       Value( const Value & rhs ) : _data( rhs._data ) {}
209
210       /** Ctor creating a JSON representation of \a T via \ref toJSON(T) */
211       template <class T>
212       Value( const T & val_r ) : _data( toJSON( val_r ) ) {}
213
214       /** JSON representation */
215       const std::string & asJSON() const
216       { return _data; }
217
218       /** String representation */
219       const std::string & asString() const
220       { return asJSON(); }
221
222       /** Stream output */
223       std::ostream & dumpOn( std::ostream & str ) const
224       { return str << _data; }
225
226     private:
227       std::string _data;
228     };
229
230     /** \relates Value Stream output */
231     inline std::ostream & operator<<( std::ostream & str, const Value & obj )
232     { return obj.dumpOn( str ); }
233
234     ///////////////////////////////////////////////////////////////////
235
236     ///////////////////////////////////////////////////////////////////
237     /// \class String
238     /// \brief JSON string
239     /// Force representation as JSON string, mapping e.g. \c null values
240     /// to an empty string. Maninly used in \ref Object as key.
241     ///////////////////////////////////////////////////////////////////
242     struct String : public Value
243     {
244       String()                          : Value( "" ) {}
245       String( std::nullptr_t )          : Value( "" ) {}
246
247       String( const char val_r )        : Value( val_r ) {}
248       String( const char * val_r )      : Value( val_r ? val_r : "" ) {}
249       String( const std::string & val_r ): Value( val_r ) {}
250     };
251
252     ///////////////////////////////////////////////////////////////////
253     /// \class Array
254     /// \brief JSON array
255     ///////////////////////////////////////////////////////////////////
256     struct Array
257     {
258       Array() {}
259
260       /** Construct from container iterator */
261       template <class Iterator>
262       Array( Iterator begin, Iterator end )
263       { for_( it, begin, end ) add( *it ); }
264
265       /** Construct from container initializer list { v1, v2,... } */
266       Array( const std::initializer_list<Value> & contents_r )
267         : Array( contents_r.begin(), contents_r.end() )
268       {}
269
270       /** Push JSON Value to Array */
271       void add( const Value & val_r )
272       { _data.push_back( val_r.asJSON() ); }
273
274       /** \overload from container initializer list { v1, v2,... } */
275       void add( const std::initializer_list<Value> & contents_r )
276       { for_( it, contents_r.begin(), contents_r.end() ) add( *it ); }
277
278       /** JSON representation */
279       std::string asJSON() const
280       { return str::Str() << *this; }
281
282       /** String representation */
283       std::string asString() const
284       { return asJSON(); }
285
286       /** Stream output */
287       std::ostream & dumpOn( std::ostream & str ) const
288       {
289         if ( _data.empty() )
290           return str << "[]";
291         str << '[' << *_data.begin();
292         for_( val, ++_data.begin(), _data.end() )
293           str << ", " << *val;
294         return str << ']';
295       }
296
297     private:
298       std::list<std::string> _data;
299     };
300
301     /** \relates Array Stream output */
302     inline std::ostream & operator<<( std::ostream & str, const Array & obj )
303     { return obj.dumpOn( str ); }
304
305     template <class V>
306     std::string toJSON( const std::vector<V> & cont_r )
307     { return json::Array( cont_r.begin(), cont_r.end() ).asJSON(); }
308
309     template <class V>
310     std::string toJSON( const std::list<V> & cont_r )
311     { return json::Array( cont_r.begin(), cont_r.end() ).asJSON(); }
312
313     template <class V>
314     std::string toJSON( const std::set<V> & cont_r )
315     { return json::Array( cont_r.begin(), cont_r.end() ).asJSON(); }
316
317     ///////////////////////////////////////////////////////////////////
318     /// \class Object
319     /// \brief JSON object
320     ///////////////////////////////////////////////////////////////////
321     struct Object
322     {
323       Object() {}
324
325       /** Construct from map-iterator */
326       template <class Iterator>
327       Object( Iterator begin, Iterator end )
328       { for_( it, begin, end ) add( it->first, it->second ); }
329
330       /** Construct from map-initializer list { {k1,v1}, {k2,v2},... } */
331       Object( const std::initializer_list<std::pair<String, Value>> & contents_r )
332         : Object( contents_r.begin(), contents_r.end() )
333       {}
334
335       /** Add key/value pair */
336       void add( const String & key_r, const Value & val_r )
337       { _data[key_r.asJSON()] = val_r.asJSON(); }
338
339       /** \overload from map-initializer list { {k1,v1}, {k2,v2},... } */
340       void add( const std::initializer_list<std::pair<String, Value>> & contents_r )
341       { for_( it, contents_r.begin(), contents_r.end() ) add( it->first, it->second ); }
342
343       /** JSON representation */
344       std::string asJSON() const
345       { return str::Str() << *this; }
346
347       /** String representation */
348       std::string asString() const
349       { return asJSON(); }
350
351       /** Stream output */
352       std::ostream & dumpOn( std::ostream & str ) const
353       {
354         using std::endl;
355         if ( _data.empty() )
356           return str << "{}";
357         dumpOn( str << '{' << endl, _data.begin() );
358         for_ ( val, ++_data.begin(), _data.end() )
359           dumpOn( str << ',' << endl, val );
360         return str << endl << '}';
361       }
362
363     private:
364       std::ostream & dumpOn( std::ostream & str, std::map<std::string,std::string>::const_iterator val_r ) const
365       { return str << val_r->first << ": " << val_r->second; }
366
367       std::map<std::string,std::string> _data;
368     };
369
370     /** \relates Object Stream output */
371     inline std::ostream & operator<<( std::ostream & str, const Object & obj )
372     { return obj.dumpOn( str ); }
373
374     template <class K, class V>
375     std::string toJSON( const std::map<K,V> & cont_r )
376     { return json::Object( cont_r.begin(), cont_r.end() ).asJSON(); }
377
378
379   } // namespace json
380   ///////////////////////////////////////////////////////////////////
381 } // namespace zypp
382 ///////////////////////////////////////////////////////////////////
383 #endif // ZYPP_BASE_JSON_H