GIT repositories

Index page of all the GIT repositories that are clonable form this server via HTTPS. Übersichtsseite aller GIT-Repositories, die von diesem Server aus über git clone (HTTPS) erreichbar sind.

Services

A bunch of service scripts to convert, analyse and generate data. Ein paar Services zum Konvertieren, Analysieren und Generieren von Daten.

GNU octave web interface

A web interface for GNU Octave, which allows to run scientific calculations from netbooks, tables or smartphones. The interface provides a web form generator for Octave script parameters with pre-validation, automatic script list generation, as well presenting of output text, figures and files in a output HTML page. Ein Webinterface für GNU-Octave, mit dem wissenschaftliche Berechnungen von Netbooks, Tablets oder Smartphones aus durchgeführt werden können. Die Schnittstelle beinhaltet einen Formulargenerator für Octave-Scriptparameter, mit Einheiten und Einfabevalidierung. Textausgabe, Abbildungen und generierte Dateien werden abgefangen und in einer HTML-Seite dem Nutzer als Ergebnis zur Verfügung gestellt.

C++ JSON parser/composer Klassentemplate

C++ JSON parser/composer class template

Liest/schreibt JSON Text von/zu string oder iostream. Siehe Beispiele (und ganz unten das kleine Testprogram). Verwende einfach die statischen Funktionen sw::json::parse(...) und sw::json::compose(...).

Features und Dinge, die man wissen sollte:

  • Parser und Composer.

  • Strict/non-strict parsing/composing (siehe unten)

  • Versteht Unicode Escapesequenzen und wandelt diese in UTF-8 um

  • Versteht (und ignoriert) Zeilenkommentare: // COMMENTS <newline>

  • Versteht (und ignoriert) Mehrzeilenkommentare: /* COMMENTS */

  • Wirft verständliche Exceptions mit Angabe von Zeile/Spalte

  • Die Formatierung des importierten JSON-"Dokuments" wird nicht gespeichert, die Daten sind aber identisch. Was ich damit meine: Die Klasse taugt zum Einlesen von JSON-Konfiguration und für den Datentransfer, nicht um "human readable" Konfigurationsdateien zu editieren.

  • Unicode Escapesequenzen (, die schon beim Parsen in UTF8 gewandelt wurden,) sind beim Schreiben ("Re-Composen") UTF8.

  • Schreibt immer komprimiert, d.h. alle überflüssigen Leerzeichen fliegen raus. Die Ausgabe ist eine Zeile.

  • Die Klasse benötigt Exceptions, also diese nicht mit -fno-exceptions abschalten. RTTI-Support wird nicht gebraucht, -fno-rtti is ok.

Reads/writes JSON fomatted text from/to string or iostream. See the examples (and the bottommost test program). Simply use the static functions sw::json::parse(...) and sw::json::compose(...).

Features and things to know about it:

  • Has Parser and composer.

  • Strict/non-strict parsing/composing (see below)

  • Understands unicode escapes, converts latter to utf8

  • Understands single line comments: // COMMENTS <newline>

  • Understands multi line comments: /* COMMENTS */

  • Throws understandable exceptions with error line/colum.

  • The 'document structure' is not saved, parse and re-compose can have different output. The data, however, are identical.

  • Comments are generally removed.

  • Parsed unicode escape sequences will be composed as UTF8.

  • Always composed in compressed form (no newlines, no spaces).

    • Use this class for reading (e.g. config files) and data exchange, not for modifying human readable JSON 'documents'.
  • Does not work with compiler option -fno-exceptions, but with -fno-rtti.

Dateien

Files

json.hh var.hh microtest/json.cc

Beispiele

Examples

#include <sw/json.hh>
#include <iostream>
#include <sstream>
 
using namespace std;
using namespace sw;
 
int main(int argc, char** argv)
{
  // Parse from string:
  sw::var a = sw::json::parse("{ a:10, b:[1,2,3], c:\"foo\" }");
 
  // Parse from stream:
  std::stringstream ss("{ a:10, b:[1,2,3], c:\"foo\" }");
  sw::var b = sw::json::parse(ss); // Could be as well std::cin or file stream.
 
  // Print results:
  cout << "-------------------------------------" << endl
       << "a= ";
 
  a.dump(cout);
  cout << endl;
 
  // Compose:
  cout << "-------------------------------------" << endl;
 
  // Strict composing
  cout << "composed (strict)= ";
  sw::json::compose(a, cout);
  cout << endl;
 
  // Non-strict composing
  cout << "composed (non-strict)= ";
  sw::json::compose(a, cout, sw::json::o_no);
  cout << endl;
 
  // Compose to string:
  string s = sw::json::compose(a, sw::json::o_no);
  cout << "composed string = " << s << endl;
}
 
// Output:
//
//  g++ -Wall -O3 -pedantic -std=c++98 main.cc -o test-main -I../ -lm
//  ./test-main
//  -------------------------------------
//  a= map(3) =
//  {
//    "a": 10
//    "b": vector(3) =
//    [
//      0: 1
//      1: 2
//      2: 3
//    ]
//    "c": foo
//  }
//
//  -------------------------------------
//  composed (strict)= {"a":10,"b":[1,2,3],"c":"foo"}
//  composed (non-strict)= {a:10,b:[1,2,3],c:"foo"}
//  composed string = {a:10,b:[1,2,3],c:"foo"}

Klassenquelltext

Class source code

/**
 * @package de.atwillys.cc.swl
 * @license BSD (simplified)
 * @author Stefan Wilhelm (stfwi)
 *
 * @json.hh
 * @ccflags
 * @ldflags
 * @platform linux, bsd, windows
 * @standard >= c++98
 *
 * -----------------------------------------------------------------------------
 *
 * JSON parser / composer class template. Requires variable type class
 * `sw::detail::basic_var<...>` / `sw::var`.
 *
 *  namespace sw { namespace detail {
 *    template <typename string_typeype> class basic_json;
 *  }}
 *
 * Default specialisation with std::string:
 *
 *  namespace sw {
 *    typedef detail::basic_json<std::string> json;
 *  }
 *
 * -----------------------------------------------------------------------------
 *
 * Brief features and annotations
 *
 *  - parser, composer.
 *  - Strict/non-strict parsing/composing (see below)
 *  - Understands unicode escapes, converts latter to utf8
 *  - Understands single line comments: `// COMMENTS <newline>`
 *  - Understands multi line comments: `/ * COMMENTS  * /`
 *  - Throws understandable exceptions with error line/colum.
 *
 *  - The 'document structure' is not saved, parse and re-compose can have
 *    different output. The data, however, are identical.
 *  - Comments are generally removed.
 *  - Parsed unicode escape sequences will be composed as UTF8.
 *  - Always composed in compressed form (no newlines, no spaces).
 *
 *    --> Use this class for reading (e.g. config files) and data exchange,
 *        not for modifying human readable JSON 'documents'.
 *
 *  - Does not work with compiler option `-fno-exceptions`, but with `-fno-rtti`.
 *
 * -----------------------------------------------------------------------------
 *
 * The main functions used are static and named `parse(...)` and `compose(...)`.
 * `parse()` can take either a `istream` reference or a const `string` reference,
 * `compose()` can either write to a `ostream` or return a `string`.
 *
 * As additional argument, options can be passed to the parser/composer, where
 * currently the only options are `o_no` (no options) and `o_strict`, which
 * means
 *
 *      - `o_strict`: The parser shall not try to "overlook" inconsistencies
 *                    but throw on everything that could mean trouble.
 *
 *      - `o_strict`: The composer must apply strict rules, means e.g. it must
 *                    quote object keys, even if they are compliant to alphanumeric
 *                    variable name conventions ( `{ "a":10 }`, NOT {a:10} ).
 *
 * The parser is non-strict by default, the composer is strict by default.
 *
 * Usage:
 *
 *  - Parse:
 *
 *     sw::var result = sw::json::parse(stream, (<options>));
 *
 *     sw::var result = sw::json::parse(string, (<options>));
 *
 *    e.g.:
 *
 *      sw::var r = sw::json::parse(cin); // strict is optional
 *      r.dump(cerr);
 *
 * - Compose:
 *
 *    sw::json::compose((const sw::var&) what, (ostream&) where); // --> ostream
 *
 *    string result = sw::json::compose((const sw::var&) what);   // --> string
 *
 * -----------------------------------------------------------------------------
 * +++ BSD license header (You know that ...) +++
 * Copyright (c) 2010-2014, Stefan Wilhelm (stfwi, <cerbero s@atwilly s.de>)
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met: (1) Redistributions
 * of source code must retain the above copyright notice, this list of conditions
 * and the following disclaimer. (2) Redistributions in binary form must reproduce
 * the above copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the distribution.
 * (3) Neither the name of atwillys.de nor the names of its contributors may be
 * used to endorse or promote products derived from this software without specific
 * prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
 * AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * -----------------------------------------------------------------------------
 */
#ifndef SW__JSON_HH
#define SW__JSON_HH
 
#include "var.hh"
#include <string>
#include <cctype>
#include <iostream>
#include <vector>
#include <stack>
#include <stdexcept>
#include <algorithm>
 
namespace sw { namespace detail {
 
template <typename StringType>
class basic_json
{
 
  //////////////////////////////////////////////////////////////////////////////
  // Types
 
public:
 
  typedef StringType string_type;
  typedef typename string_type::value_type char_type; // Char type
  typedef unsigned int ucchar_t; // Unicode character type, >= uint16
  typedef std::basic_istream<char_type> istream_type;
  typedef std::basic_ostream<char_type> ostream_type;
  typedef std::basic_stringstream<char_type> sstream_type;
  typedef basic_var<string_type, typename sw::var::float_type, typename sw::var::int_type> var_type;
  typedef std::runtime_error exception_type;
  typedef enum { o_no=0, o_strict } options_type;
 
  //////////////////////////////////////////////////////////////////////////////
  // Static "main functions"
 
public:
 
  /**
   * Parses a JSON text stream into an sw::var variable.
   * @param istream_type& stream
   * @param bool strict=false
   * @return var_type
   */
  static inline var_type parse(istream_type& stream, bool strict=false)
  { var_type r; basic_json()(stream, r, strict ? o_strict:o_no); return r; }
 
  /**
   * Parses a JSON text into an sw::var variable.
   * @param const string_type& text
   * @param bool strict=false
   * @return var_type
   */
  static inline var_type parse(const string_type& txt, bool strict=false)
  { var_type r; sstream_type s(txt); basic_json()(s, r, strict ? o_strict:o_no ); return r; }
 
  /**
   * Composes JSON to a given stream. Note that comments are omitted and the
   * order of object key-value pairs can differ from a JSON string parsed
   * before.
   * @param const var_type& v
   * @param ostream_type& stream
   * @param options_type opts = o_strict
   * @return ostream_type& stream
   */
  static inline ostream_type& compose(const var_type& v, ostream_type& stream, options_type opts=o_strict)
  { basic_json()(v, stream, opts); return stream; }
 
  /**
   * Composes JSON to a given string. Note that comments are omitted and the
   * order of object key-value pairs can differ from a JSON string parsed
   * before.
   * @param const var_type& v
   * @param ostream_type& stream
   * @param options_type opts = o_strict
   * @return ostream_type& stream
   */
  static inline string_type compose(const var_type& v, options_type opts=o_strict)
  { sstream_type s; basic_json()(v, s, opts); return s.str(); }
 
 
  //////////////////////////////////////////////////////////////////////////////
  // Construction / destruction
 
public:
 
  /**
   * Default constructor
   */
  inline basic_json() :
    is_(NULL), os_(NULL), line_(1), line_pos_(1), eof_overread_('\n'), opts_(o_no)
  { ; }
 
  /**
   * Destructor
   */
  virtual ~basic_json()
  { ; }
 
  //////////////////////////////////////////////////////////////////////////////
  // "Main methods" exposed as operator (), getters.
 
public:
 
  /**
   * Parses a JSON text stream into an sw::var variable.
   * @param istream_type& stream
   * @param bool strict=false
   * @param var_type& result
   * @return var_type&
   */
  inline var_type& operator() (istream_type& stream, var_type& result, options_type opts=o_no)
  { is_ = &stream; opts_=opts; parse_root(result); return result; }
 
  /**
   * Composes JSON to a given stream. Note that comments are omitted and the
   * order of object key-value pairs can differ from a JSON string parsed
   * before.
   * @param const var_type& v
   * @param ostream_type& stream
   * @param options_type opts = o_strict
   * @return ostream_type& stream
   */
  inline ostream_type& operator() (const var_type& v, ostream_type& stream, options_type opts=o_strict)
  { os_ = &stream; opts_=opts; compose_root(v); return stream; }
 
  /**
   * True if strict parsing/composing.
   * @return bool
   */
  inline bool strict() const
  { return (opts_ & o_strict) == o_strict; }
 
 
  //////////////////////////////////////////////////////////////////////////////
  // Parse
 
protected:
 
  inline bool eof() const
  { return is_->eof(); }
 
  /**
   * Get next char from stream. (_is must be assigned!). Throw exception
   * on bad stream. Returns 0 on EOF.
   * @return ch_t
   */
  inline char_type get()
  {
    char_type c;
    if(!is_->get(c)) {
      if(!is_->eof()) {
        throw exception_type("Read error");
      } else if(eof_overread_ != '\0') {
        // We accept one char overread before throwing.
        c = '\0'; eof_overread_ = '\0';
      } else {
        throw exception_type("Unexpected end of input");
      }
    } else {
      if(c == '\n') { line_++; line_pos_=0; }
      line_pos_++;
    }
    return c;
  }
 
  /**
   * Take a glance at the next character without pulling it out of the stream.
   * @return char_type
   */
  inline char_type peek() const
  { char_type c=is_->peek(); return (c==istream_type::traits_type::eof()) ? 0:c; }
 
  /**
   * Put a character back into the stream. Decrements position but does not
   * unwrap line counter.
   * @param char_type c
   */
  inline void putback(char_type c)
  { if(c) { line_pos_--; is_->putback(c); } }
 
  /**
   * Skips spaces
   */
  inline void skip_spaces()
  { while(is_->good() && std::isspace(peek()) && get()); }
 
 
  /**
   * Detects // and / * * / comments and skips them.
   */
  void skip_comments()
  {
    // Could be multiple comments joined, eventually get() will force abort
    // on eof().
    while(!eof()) {
      skip_spaces();
      if(peek() != '/') return;
      char_type c = get();
      char_type c1 = peek();
      if((c1 != '/') && (c1 != '*')) { putback(c); return; }
      if(c1 == '/') {
        while((c=get()) && c!='\n' && c!='\r');
        if(eof()) { eof_overread_ = '\n'; return; }
      } else {
        while((c=get()) && !(c=='*' && peek()=='/'));
        get();
      }
    }
  }
 
  /**
   * Starting point of the whole parse process
   * @param var_type &node
   */
  void parse_root(var_type &node)
  {
    try {
      eof_overread_ = '\n';
      line_ = 1;
      line_pos_ = 1;
      node.clear();
      if(!is_) throw exception_type("Input stream pointer is NULL. Can't parse.");
      skip_comments();
      if(eof()) return; // Empty input --> var will be `unset`.
      parse_any(node);
      skip_comments();
      if(!eof()) throw exception_type("End of input expected");
    } catch(const exception_type &e) {
      try { node.clear(); } catch(...) { ; }
      sstream_type ss; ss << "JSON parser [@" << line_ << ":" << line_pos_ << "]: " << e.what();
      throw exception_type(ss.str());
    }
  }
 
  /**
   * Relay for parsing others.
   * @param var_type &node
   */
  void parse_any(var_type &node)
  {
    skip_comments();
    char_type c = get();
 
    if(c == '"') {
      parse_string(node);
    } else if(c == '[') {
      parse_array(node);
    } else if(c == '{') {
      parse_object(node);
    } else if(std::isdigit(c) || c=='-' || c=='+' || c=='.') {
      putback(c); parse_number(node);
    } else if(std::isalpha(c) || c=='_') {
      putback(c); parse_literal(node);
    } else if(c != 0) {
      throw exception_type(string_type("Unexpected character '")+c+"'");
    } else {
      node.clear();
    }
  }
 
  /**
   * Parses a number, assigns either int or floating point value to the node.
   * Throws on error.
   * @param var_type &node
   */
  void parse_number(var_type &node)
  {
    sstream_type ss;
    char_type c;
    bool is_float = false;
    if(peek() == '+') get();
    while((c = get())) {
      if(std::isdigit(c)) {
        ss << c;
      } else if(c=='.' || c=='e' || c=='E' || c=='+' || c=='-') {
        ss << c;
        is_float = true;
      } else {
        putback(c);
        break;
      }
    }
    double d;
    string_type s = ss.str();
    if(!(ss >> d) || !ss.eof()) { // Expected: Whole string is the number
      node.f(std::numeric_limits<double>::quiet_NaN());
      throw exception_type(string_type("Invalid numeric expression: '")+s+"'");
    } else if(is_float) {
      node.f(d);
    } else {
      node.i((typename var_type::int_type)d);
    }
  }
 
  /**
   * Parse known literals (case insensitively). Throw on error.
   * @param var_type &node
   */
  void parse_literal(var_type &node)
  {
    char_type c = get();
    if(!std::isalpha(c) && c != '_') {
      throw exception_type("Bug: Can't parse a literal not starting with an `alnum`!");
    }
    std::string s;
    s.reserve(16);
    s += c;
    while((c = get()) && (std::isalnum(c) || c=='_')) s += c;
    putback(c);
    std::string ls(s);
    std::transform(s.begin(), s.end(), s.begin(), ::tolower);
    if(s == "null") {
      node = var_type::nul();
    } else if(s == "false") {
      node.b(false);
    } else if(s == "true") {
      node.b(true);
    } else if(!strict() && s == "undefined") {
      node.clear();
    } else {
      throw exception_type(string_type("Unknown literal '") + ls + "'");
    }
  }
 
  /**
   * Parse array. Expected that the leading '[' literal is already shifted off.
   * Throws on error.
   * @param var_type &node
   */
  void parse_array(var_type &node)
  {
    node.v(var_type::empty_vector());
    skip_comments();
    if(peek() == ']') { get(); return; }
    while(true) {
      var_type v;
      parse_any(v);
      node.push_back(v);
      skip_comments();
      char_type c = get();
      if(c == ']') break;
      if(c != ',') throw exception_type("Expected ']' or ',' while parsing array");
    }
  }
 
  /**
   * Parse object. Expected that the leading '{' literal is already shifted off.
   * Throws on error.
   * @param var_type &node
   */
  void parse_object(var_type &node)
  {
    node.m(var_type::empty_map());
    skip_comments();
    if(peek() == '}') { get(); return; }
    while(true) {
      string_type key = parse_object_key();
      skip_comments();
      if(get() != ':') throw exception_type("Missing ':' after object key");
      var_type v;
      parse_any(v);
      node.m(key, v);
      skip_comments();
      char_type c = get();
      if(c == '}') break;
      if(c != ',') throw exception_type("Expected '}' or ',' while parsing object");
    }
  }
 
  /**
   *
   * @return
   */
  string_type parse_object_key()
  {
    skip_comments();
    char c = get();
    if(c == '"') {
      var_type v;
      parse_string(v);
      return v.s();
    } else if(std::isalpha(c) || c=='_' || (!strict() && std::isdigit(c))) {
      string_type s; s.reserve(32);
      s += c;
      while((c=get()) && (std::isalnum(c) || c=='_')) s += c;
      if(!std::isspace(c)) putback(c);
      return s;
    } else {
      throw exception_type(string_type("Invalid object key start character '")+c+"'");
    }
  }
 
  /**
   * Parse string. Expected that the beginning literal is already shifted off
   * the stream.
   * @param var_type &node
   */
  void parse_string(var_type &node)
  {
    sstream_type ss;
    char_type c;
    while((c = get())) {
      switch(c) {
        case '"':
          node.s(ss.str());
          return;
          break;
        case '\\':
          switch(peek()) {
            case '\\':
            case '"' :
            case '/' : ss << get(); break;
            case 'n' : ss << "\n"; get(); break;
            case 'r' : ss << "\r"; get(); break;
            case 't' : ss << "\t"; get(); break;
            case 'u' : putback(c); ss << parse_unicode_escape_to_utf8(); break;
            case 'v' : ss << "\v"; get(); break;
            case 'f' : ss << "\f"; get(); break;
            case 'b' : ss << "\b"; get(); break;
            case 'a' : ss << "\a"; get(); break;
            default  :
              if(!strict()) {
                ss << '\\';
              } else {
                throw exception_type(string_type("Unexpected escape character '\\")+c+"'");
              }
          }
          break;
        default:
          ss << c;
      }
    }
    throw exception_type("Unexpected end of file while parsing string.");
  }
 
  /**
   * Returns utf8 string from one or more (concatenated) unicode escape
   * sequences.
   * @return string_type
   */
  string_type parse_unicode_escape_to_utf8()
  {
    string_type s; s.reserve(8);
    while(peek()=='\\') {
      ucchar_t uc;
      char_type c = get();
      if(peek() != 'u') { putback(c); break; }
      get();
      uc = parse_ucc_esc();
      if(uc >= 0xd800 && uc <= 0xdbff) { // It's a surrog. pair?
        if(get()!='\\' || get()!='u') {
          throw exception_type("Missing second unicode escape sequence for surrogate pair.");
        }
        ucchar_t uc2 = parse_ucc_esc();
        uc = ((1ul)<<16) + ((uc & (((1ul<<10)-1)<<10)) | (uc2 & ((((1ul)<<10)-1))));
      }
      s += ucc2utf8(uc);
    }
    return s;
  }
 
  ucchar_t parse_ucc_esc()
  {
    ucchar_t uc = 0;
    char_type c;
    for(int i=0; i<4; ++i) {
      c = ::tolower(get());
      uc <<= 4;
      if     (c>='0' && c<='9') uc|= (c-'0');
      else if(c>='a' && c<='f') uc|= (c-'a'+10);
      else throw exception_type("Invalid hex character while parsing unicode escape.");
    }
    return uc;
  }
 
  static string_type ucc2utf8(ucchar_t c)
  {
    unsigned char s[5] = {0,0,0,0,0};
    if(c < 0x00000080) {
       s[0] = (c);
    } else if (c < 0x00000800u) {
       s[0] = (0xc0u | (0x1fu & (c >>  6)));
       s[1] = (0x80u | (0x3fu & (c >>  0)));
    } else if (c < 0x00010000u) {
       s[0] = (0xE0u | (0x0fu & (c >> 12)));
       s[1] = (0x80u | (0x3fu & (c >>  6)));
       s[2] = (0x80u | (0x3fu & (c >>  0)));
    } else if (c < 0x00110000u) {
       s[0] = (0xf0u | (0x07u & (c >> 18)));
       s[1] = (0x80u | (0x3fu & (c >> 12)));
       s[2] = (0x80u | (0x3fu & (c >>  6)));
       s[3] = (0x80u | (0x3fu & (c >>  0)));
    }
    return string_type((char*)s);
  }
 
  //////////////////////////////////////////////////////////////////////////////
  // Compose
 
protected:
 
  inline ostream_type & put()
  { if(os_->good()) return *os_; throw exception_type("Compose output stream failed"); }
 
  /**
   * Compose starting point.
   * @param const var_type&
   * @param ostream_type& os
   */
  void compose_root(const var_type& o)
  {
    try {
      line_ = line_pos_ = 0;
      eof_overread_ = 0;
      is_ = 0;
      if(o.is_unset()) return;
      if((!os_) ||  !os_->good()) throw exception_type("Cannot compose, out stream is 'bad'");
      compose_r(o);
    } catch(const exception_type &e) {
      sstream_type s; s << "JSON composer: " << e.what();
      throw exception_type(s.str());
    }
  }
 
  /**
   * Recursive composing.
   * @param const var_type&
   * @param ostream_type& os
   */
  void compose_r(const var_type& o)
  {
    if(o.is_unset()) {
      return;
    } else if(o.is_null()) {
      put() << "null";
    } else if(o.is_bool()) {
      put() << (o.b() ? "true" : "false");
    } else if(o.is_int()) {
      put() << o.i();
    } else if(o.is_float()) {
      put() << o.f();
    } else if(o.is_string()) {
      compose_string(o.s());
    } else if(o.is_vector()) {
      put() << "[";
      int end = o.size()-1;
      int i=-1;
      while(++i < end) { compose_r(o.v(i)); put() << ","; }
      if(i<=end) compose_r(o.v(i));
      put() << "]";
    } else if(o.is_map()) {
      put() << "{";
      typename var_type::map_t::const_iterator end = o.m().end();
      for(typename var_type::map_t::const_iterator it=o.m().begin(); it!=end; ++it) {
        compose_object_key(it->first);
        put() << ":";
        compose_r(it->second);
        typename var_type::map_t::const_iterator cit = it;
        if(++cit != end)put() << ",";
      }
      put() << "}";
    }
 
  }
 
  /**
   * Normally identically to compose_string. If nonstrict, alphanumeric keys
   * are not escaped using string literals.
   * @param const string_type &s
   */
  void compose_object_key(const string_type &s)
  {
    if(s.empty()) {
      put() << "\"\"";
    } else if(!strict() && std::isalpha(s[0])) {
      typename string_type::const_iterator it=s.begin();
      typename string_type::const_iterator end=s.end();
      while((++it)!=end) {
        if(!std::isalnum(*it) && (*it) != '_') {
          compose_string(s);
          return;
        }
      }
      put() << s;
    } else {
      compose_string(s);
    }
  }
 
  /**
   * String composition
   * @param const string_type &s
   * @return void
   */
  void compose_string(const string_type &s)
  {
    put() << "\"";
    typename string_type::const_iterator p=s.begin();
    typename string_type::const_iterator e=s.end();
    for(; p!=e; ++p) {
      if((*p) < 0) {
        (*os_) << (*p); // pass through UTF8, no stream bad check here
      } else if((*p) == 0) {
        put() << ucc2uchex(0); // Just to ensure ...
      } else if((*p) != ' ' && (std::iscntrl(*p) || std::isspace(*p))) {
        switch((*p)) {
          case '\n': (*os_) << "\\n"; break;
          case '\r': (*os_) << "\\r"; break;
          case '\t': (*os_) << "\\t"; break;
          case '\f': (*os_) << "\\f"; break;
          case '\v': (*os_) << "\\v"; break;
          case '\a': (*os_) << "\\a"; break;
          case '\b': (*os_) << "\\b"; break;
          default:   (*os_) << ucc2uchex(((1u<<7)-1) & (*p));
        }
      } else if((*p)=='\\' || (*p)=='"') {
        (*os_) << '\\' << (*p);
      } else {
        (*os_) << (*p); // pass through normal char, no stream bad check here
      }
    }
    put() << "\"";
  }
 
  /**
   * Note: Stripped version, no surrogates
   * @param unsigned uc
   * @return string_type
   */
  inline string_type ucc2uchex(unsigned uc)
  {
    uc &= (1u<<16)-1;
    char s[7] = { '\\','u','0','0','0','0', 0};
    int i = 6;
    while(i<7 && uc!=0) { // uint wrap -> i<0
      char c = (uc & 0x0f);
      uc >>= 4;
      s[--i] = (c<10) ? (c+'0') : (c-10+'a');
    }
    return s;
  }
 
  //////////////////////////////////////////////////////////////////////////////
  // Instance variables
 
protected:
 
  istream_type *is_;
  ostream_type *os_;
  int line_, line_pos_;
  char_type eof_overread_;
  options_type opts_;
};
 
}}
 
 
////////////////////////////////////////////////////////////////////////////////
// Default specialisation
 
namespace sw {
  typedef detail::basic_json<std::string> json;
}
 
#endif

Mehr Beispiele

More Examples

#include <json.hh>
#include "test.hh"
 
using namespace std;
 
void parse(string s, const sw::json::var_type &expect=sw::json::var_type::undef())
{
  sw::json::var_type json;
  try {
    std::string sout;
    {
      std::stringstream ssout;
      ssout << "JSON  : '" << s << "'" << endl << "OUTPUT: ";
      json = sw::json::parse(s);
      json.dump(ssout);
      sout = ssout.str();
      while(sout.length() > 1 && ::isspace(sout[sout.length()-1])) { // std98: no .back()
        sout.resize(sout.length()-1);
      }
    }
    test_comment("Check: " << endl << sout);
    if(expect.is_unset() || expect == "<IGNORE>") {
      test_pass("Ok    : yes (no expectation/ignored)");
    } else if(expect == "<EXCEPTION>") {
      test_fail("Ok    : no (exception was expected)");
    } else if(json == expect) {
      test_pass("Ok    : yes");
    } else {
      test_fail("Ok    : no");
    }
  } catch(const sw::json::exception_type &e) {
    test_comment("Exception: " << e.what());
    if(expect == "<EXCEPTION>") {
      test_pass("Ok    : yes (expected exception)");
    } else if(expect == "<IGNORE>") {
      test_pass("Ok    : yes (ignored)");
    } else {
      test_fail(std::string("Ok    : no, exception: ") + e.what());
    }
  } catch(...) {
    test_fail("Ok    : no (unexpected exception type)");
  }
}
 
void compose(const string &json_parse, const string &expect="<IGNORE>",
    sw::json::options_type opts=sw::json::o_strict)
{
  try {
    std::stringstream sout;
    sout << "JSON IN : '" << json_parse << "'" << endl
         << "EXPECT  : '" << expect << "'" << endl
         << "JSON OUT: ";
    sw::json::var_type j = sw::json::parse(json_parse);
    string s = sw::json::compose(j, opts);
    sout << "'" << s << "'";
    test_comment("Composed: " << endl << sout.str());
    if(expect == "<IGNORE>") {
      test_pass("Ok      : yes (no expectation/ignored)");
    } else if(expect == s) {
      test_pass("Ok      : yes");
    } else {
      test_fail("Ok      : no");
    }
  } catch(const sw::json::exception_type& e) {
    test_fail(std::string("Ok      : no (exception ") + e.what() + ")");
  } catch(...) {
    test_fail("Ok      : no (unexpected exception)");
  }
 
}
 
////////////////////////////////////////////////////////////////////////////////
 
void test_num()
{
  parse("12345", 12345);
  parse("12.345", 12.345);
  parse("12.34e5", 12.34e5);
  parse("12.3E5", 12.3e5);
  parse("1.2e-5", 1.2e-5);
  parse("-1.2e-5", -1.2e-5);
  parse("+1.2e-5", 1.2e-5);
  parse("1.2e+5", 1.2e5);
  parse(".2e-5", 0.2e-5);
  parse(".1", 0.1);
  parse("  12345  ", 12345);
  parse("12.34.5", "<EXCEPTION>");
  parse("12.34r5", "<EXCEPTION>");
  parse("12.3E4e5", "<EXCEPTION>");
 
}
 
void test_literal()
{
  parse("null", sw::var::nul());
  parse("true", true);
  parse("false", false);
  parse(" null", sw::var::nul());
  parse("Nulll", "<EXCEPTION>");
  parse("NULL", sw::var::nul());
}
 
void test_string()
{
  parse(" \"Hello\" ", "Hello");
  parse(" \"He\\\"llo\" ", "He\"llo");
  parse(" \"He\\\\\\\"llo\" ", "He\\\"llo");
  parse(" \"Hello\\\" ", "<EXCEPTION>");
  parse(" \"Hello\\\" ", "<EXCEPTION>");
 
  parse(" \"\\n\" ", "\n");
  parse(" \"\\r\" ", "\r");
  parse(" \"\\t\" ", "\t");
  parse(" \"\\f\" ", "\f");
  parse(" \"\\a\" ", "\a");
  parse(" \"\\v\" ", "\v");
  parse(" \"\\b\" ", "\b");
  parse(" \"\\\\\" ", "\\");
}
 
void test_array()
{
  parse("[]");
  parse("[ 1, 2, 3, 4, 5 ]");
  parse("[ 1, \"A\", null, false, 5.5 ]");
  parse(" [1,\"A\",null,false,5.5] ");
  parse(" [\"A\", [], [1,2,3], [4,5,6] ] ");
  parse("[[[[[[[[1]]]]]]]]");
 
}
 
void test_object()
{
  parse("{}");
  parse("{ \"a\" : 10  }");
  parse("{a:10}");
  parse("{  a  :  \"Hello\"  }");
  parse("{a:1,b:2,c:3}");
  parse("{a:{b:1,c:2,d:3}, e:{f:4,g:5.5}, h:{}, i : { } }");
}
 
void test_combined()
{
  parse(
   "\n{ // This is an object                               \n"
   "a: [ 1, 2.1e5, .3, {a_1:1,a_2:2}], // a: is an array   \n"
   "b: { \"b1\":\"b1 value\" } ,       // b: is an object  \n"
   "c: [false /* means 0 */, true, /*>*/null/*<*/, \"$\"] // c: array as well \n"
   "\n}\n"
  );
 
}
 
void test_comments()
{
  parse("// Comment ignored\n\n");
  parse("// REM\n //REM\n 10 //REM");
  parse("[ // REM\n 10, //REM\n 20 //REM \n ]");
  parse("/**/");
  parse("/*REM*/");
  parse("10/*REM*/");
  parse("/*REM*/10");
  parse("/*R/E/M*/10");
  parse("/*R//E//M*/10");
  parse("// /*REM*/\n10");
  parse("[ /*<<*/10/*>>*/, 20 //COMMENT\n, /*//*/30/*Value30*/ ]");
  parse("/**/4/**/0", "<EXCEPTION>");
}
 
////////////////////////////////////////////////////////////////////////////////
 
void test_compose_basic()
{
  compose("", "");
  compose("null", "null");
  compose("true", "true");
  compose("false", "false");
  compose("\"\"", "\"\"");
  compose("[]", "[]");
  compose("{}", "{}");
  compose("1234", "1234");
  compose("1.234", "1.234");
  compose("1e3", "1000");
  compose(".1", "0.1");
}
 
void test_compose_array()
{
  compose("[]", "[]");
  compose("[1]", "[1]");
  compose("[1,2]", "[1,2]");
  compose("[[],[]]", "[[],[]]");
  compose("[[],[[1]]]", "[[],[[1]]]");
}
 
void test_compose_object()
{
  compose("{}", "{}");
  compose("{a:1}", "{\"a\":1}");
  compose("{a:1}", "{a:1}", sw::json::o_no);
  compose("{1:1}", "{\"1\":1}", sw::json::o_no);
  compose("{_:1}", "{\"_\":1}", sw::json::o_no);
  compose("{_a:1}", "{\"_a\":1}", sw::json::o_no);
  compose("{abcdefghijklmnopqrstuvwxyz01234567890aaaaaaaaaaaaaaaaaaaaaaaaaaaa:1}",
      "{\"abcdefghijklmnopqrstuvwxyz01234567890aaaaaaaaaaaaaaaaaaaaaaaaaaaa\":1}");
  compose("{a_1:1}", "{a_1:1}", sw::json::o_no);
}
 
void test_compose_string()
{
  compose("\"\"", "\"\"");
  compose("\"H\"", "\"H\"");
  compose("\"Hello\"", "\"Hello\"");
  compose("\" \\\\ \"", "\" \\\\ \"");
  compose("\" \\n \"", "\" \\n \"");
 
  compose("\" \\u0009 \"", "\" \\t \"");
  compose("\" \\u000a \"", "\" \\n \"");
  compose("\" \\u000d \"", "\" \\r \"");
  compose("\" \\u000b \"", "\" \\v \"");
  compose("\" \\u0007 \"", "\" \\a \"");
  compose("\" \\u000c \"", "\" \\f \"");
}
 
void test_compose_combined()
{
  compose("[1,{a:1},2.2,.3,\"\"]", "[1,{\"a\":1},2.2,0.3,\"\"]");
  compose("{c:15,b:10,a:5}", "{a:5,b:10,c:15}", sw::json::o_no);
}
 
////////////////////////////////////////////////////////////////////////////////
 
void test()
{
  test_num();
  test_literal();
  test_string();
  test_array();
  test_object();
  test_comments();
  test_combined();
  test_compose_basic();
  test_compose_array();
  test_compose_object();
  test_compose_string();
  test_compose_combined();
}