1. 程式人生 > 其它 >Qt寫一個惠爾頓網路自動登入程式

Qt寫一個惠爾頓網路自動登入程式

QT做一個惠爾頓自動登入程式。

出於某種要求,公司使用了這個惠爾頓網路安全審計系統。

關鍵是這個做得不太行吧,提供的GUI程式,又大又難用(搞不懂,簡單的東西做這麼複雜)。

還提供了網頁登入程式,這個就簡單,只需要輸入賬號密碼點選登入即可。

但是吧,他喵的,必須保持登入頁面開啟,也就是一直佔用那個瀏覽器的一個標籤頁。

這他喵的也很噁心了,尤其是針對瀏覽器使用和切換都很頻繁的人來說。

是可忍,孰不可忍。使用fidlder一抓包,一Compaser post一下,就發現了登入的網頁,以及引數。

POST http://192.168.0.98/user-login-auth HTTP/1.1
Host: 192.168.0.98
Content-Type: application/x-www-form-urlencoded; charset=UTF-8

param%5BUserName%5D=username&param%5BUserPswd%5D=123456


網頁版確實簡單,可以精簡到簡單的post賬號和密碼就能驗證通過,當然,需要對一些字元進行url編碼

沒什麼好分析的,彈需求。

1.越簡單越好,我們只想使用這軟體一次,以後再也不用自己輸出賬號密碼,然後點選登入。
那就需要讀寫檔案,儲存賬號密碼。//簡單起見,自己構造個json檔案即可
那就需要開機自動執行。//登錄檔操作
2.雖然我沒遇見過,但是他們反映使用過程中,雖然登入成功,但是後面還是斷網了。也就是連線可能不穩定。
那就需要不停的檢測網路連線,斷網就需要自己post登入。
3.雖然直接打包成壓縮檔案也能用,但是使用的物件不全是程式設計師,要是隨便的移動軟體,豈不是多了很多登錄檔垃圾。
那就弄成安裝包吧。

主題程式碼如下:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QDebug>
#include <QNetworkRequest>
#include <QMessageBox>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QFile>
#include <iostream>
#include <QCloseEvent>
#include <QTimer>
//#define DEBUG
#define QS(x) QString::fromUtf8(x)
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    bool checkFile();
    void autoLogin(QString un,QString ps);
    void CreateSystemTrayIcon();
private slots:
    void on_pushButton_clicked();
    void requestFinished(QNetworkReply*);

    void on_lineEdit_2_returnPressed();

private:
    Ui::Widget *ui;
    QString success;
    QString fail;
    QString username = "";
    QString password = "";
    QTimer time;
#ifdef DEBUG
    const QString LOGIN = "http://192.168.0.102:90/test.html";
#else
    const QString LOGIN="http://192.168.0.98/user-login-auth";
#endif
protected:
    void closeEvent(QCloseEvent *event);//由於要關閉視窗變為隱藏至托盤圖示,所以要重寫close事件

};
#endif // WIDGET_H

#include "widget.h"
#include "ui_widget.h"
#include "jsonxx.h"
#include "jsonxx.cc"
#include <qnetworkproxy.h>
#include <QTextCodec>
#include <QMenu>
#include <QSystemTrayIcon>
#include <QAction>
#include <QSettings>
using namespace std;
using namespace jsonxx;
#define AUTO_RUN_KEY	"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"
//#define DEBUG
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    success = "color:green";
    fail = "color:red";
    time.setInterval(15000);//15s自動登入一次
    QObject::connect(&time, &QTimer::timeout, [=]()
    {
        autoLogin(username,password);
    });
    if(checkFile())
    {
        //隱藏到托盤
        CreateSystemTrayIcon();
    }
}

Widget::~Widget()
{
    delete ui;
}

//讀取配置檔案
bool Widget::checkFile()
{

    QFile file("config.txt");
    if(!file.open(QIODevice::ReadOnly|QIODevice::Text))
    {
        qDebug()<<file.errorString();
        return false;
    }
    else
    {
        qDebug()<<"openok";
    }
    QByteArray tt = file.read(1024);
    QString tta;
    tta = tta.fromLocal8Bit(tt);
    string str = tta.toStdString();
    Object o;
    if(o.parse(str))
    {
        QString username = "";
        QString password = "";
        QString tmp = "";
        qDebug()<<"success to parse";
        if(!o.has<String>("un"))
        {
            return false;
        }
        str = o.get<String>("un");
        username = QString::fromStdString(str);
        ui->lineEdit->setText(username);
        if(!o.has<String>("ps"))
        {
            return false;
        }
        str = o.get<String>("ps");
        password = QString::fromStdString(str);
        ui->lineEdit_2->setText(password);
        if(!o.has<String>("autoLogin"))
        {
            qDebug()<<"meiyou";
            return false;
        }
        str = o.get<String>("autoLogin");
        tmp = QString::fromStdString(str);
        if("true"!=tmp){
            return false;
        }
        ui->checkBox->setCheckState(Qt::Checked);
        autoLogin(username,password);
    }else{
        return false;
    }
}

void Widget::autoLogin(QString un, QString ps)
{
    QNetworkRequest request;
    QNetworkAccessManager* naManager = new QNetworkAccessManager(this);
    QMetaObject::Connection connRet = QObject::connect(naManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*)));
    Q_ASSERT(connRet);
    request.setUrl(QUrl(LOGIN));
    request.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded");
    QByteArray postData;
#ifdef DEBUG
    QNetworkProxy proxy;
    proxy.setHostName("127.0.0.1");
    proxy.setPort(8888);
    naManager->setProxy(proxy);
#endif
    QString s = QUrl::toPercentEncoding("param[UserName]") + "=" + QUrl::toPercentEncoding(un) + "&" + QUrl::toPercentEncoding("param[UserPswd]") + "=" + QUrl::toPercentEncoding(ps);
    postData.append(s);
    qDebug()<<s<<endl;
    naManager->post(request,postData);
    username = un;
    password = ps;
}

void Widget::CreateSystemTrayIcon()
{
    QAction* show = new QAction(QS("開啟介面"));
    QAction* login = new QAction(QS("登入"));
    QAction* quit = new QAction(QS("退出"));

    connect(show, &QAction::triggered, this, [=]()
    {
        this->showNormal();
    });
    connect(login, &QAction::triggered, this, [=]()
    {
        if(!username.isEmpty()&&!password.isEmpty())
        {
            this->autoLogin(username,password);
        }
        else
        {
            on_pushButton_clicked();
        }
    });
    connect(quit , &QAction::triggered, this, [=]()
    {
        qDebug()<<"exit";
        QApplication::exit(0);
    });

    QMenu* trayMenu = new QMenu(this);//選單
    trayMenu->addAction(show);
    trayMenu->addAction(login);
    trayMenu->addAction(quit);

    //建立一個系統托盤
    QSystemTrayIcon* trayIcon = new QSystemTrayIcon(this);
    trayIcon->setIcon(QIcon(QPixmap(":/photo/pic.ico")));
    trayIcon->setContextMenu(trayMenu);//設定選單
    trayIcon->show();
    connect(trayIcon, &QSystemTrayIcon::activated, this, [=](QSystemTrayIcon::ActivationReason reason)
    {
        qDebug()<<reason;
        qDebug()<<QApplication::mouseButtons();
        if(reason == QSystemTrayIcon::Trigger&&QApplication::mouseButtons()==Qt::LeftButton)
        {
            this->showNormal();
        }
        else
        {
            qDebug()<<"沒到條件";
        }
    });

}

void Widget::requestFinished(QNetworkReply * reply)
{
    QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
    if(statusCode.isValid())
    {
        qDebug() << "status code=" << statusCode.toInt();
    }

    QVariant reason = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
    if(reason.isValid())
    {
        qDebug() << "reason=" << reason.toString();
#ifdef DEBUG
        if(reason.toString().contains("allowed",Qt::CaseInsensitive))
        {
            qDebug()<<"success";
            ui->label_3->setStyleSheet(success);
            ui->label_3->setText(QS("登入成功"));
            time.start();
        }
        else
        {
            qDebug()<<"fail";
            ui->label_3->setStyleSheet(fail);
            ui->label_3->setText(QS("登入失敗"));
            time.stop();
            username = "";
            password = "";
            this->showNormal();
        }
#endif
    }

    QNetworkReply::NetworkError err = reply->error();
    if(err != QNetworkReply::NoError) {
        qDebug() << "Failed: " << reply->errorString();//彈出警告框
        ui->label_3->setStyleSheet(fail);
        ui->label_3->setText(QS("登入失敗"));
        username = "";
        password = "";
        time.stop();
        this->showNormal();
        return;
    }
    else {
        QString s = reply->readAll();
        if(s.contains("success",Qt::CaseInsensitive))//不區分大小寫
        {
            ui->label_3->setStyleSheet(success);
            ui->label_3->setText(QS("登入成功"));
            time.start();
        }
        else
        {
            ui->label_3->setStyleSheet(fail);
            ui->label_3->setText(QS("登入失敗"));
            username = "";
            password = "";
            time.stop();
            this->showNormal();
        }
    }
}


void Widget::on_pushButton_clicked()
{
    bool check = ui->checkBox->checkState();
    QString username = ui->lineEdit->text();
    QString password = ui->lineEdit_2->text();
    if(username.isEmpty())
    {
        QMessageBox::information(NULL,QS("提示"),QS("請輸入賬號"),QMessageBox::Ok);
        return;
    }
    if(password.isEmpty())
    {
        QMessageBox::information(NULL,QS("提示"),QS("請輸入賬號"),QMessageBox::Ok);
        return;
    }
    qDebug()<<username<<endl;
    qDebug()<<password<<endl;
    if(check)
    {
        QString str = "";
        str = "{'un':'"+username+"','ps':'"+password+"','autoLogin':'true'}";
        QFile file("config.txt");
        if(!file.open(QIODevice::WriteOnly | QIODevice::Text))
        {
            return;
        }
        QByteArray data = str.toLocal8Bit();
        file.write(data);
        qDebug()<<"length = "<<data.length()<<endl;
        file.close();
        QString application_name = QApplication::applicationName();//獲取應用名稱
        QSettings *settings = new QSettings(AUTO_RUN_KEY, QSettings::NativeFormat);//建立QSetting, 需要新增QSetting標頭檔案
        QString application_path = QApplication::applicationFilePath();//找到應用的目錄
        settings->setValue(application_name, application_path.replace("/", "\\"));//寫入登錄檔
    }
    autoLogin(username,password);
}

void Widget::on_lineEdit_2_returnPressed()
{
    on_pushButton_clicked();
}

void Widget::closeEvent(QCloseEvent *event)
{
    hide();//隱藏視窗
    event->ignore();//忽略關閉事件
}

jsonxx.h

// -*- mode: c++; c-basic-offset: 4; -*-

// Author: Hong Jiang <[email protected]>
// Contributors:
//   Sean Middleditch <[email protected]>
//   rlyeh <https://github.com/r-lyeh>

#pragma once

#include <cstddef>
#include <cassert>
#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <sstream>

// jsonxx versioning: major.minor-extra where
// major = { number }
// minor = { number }
// extra = { 'a':alpha, 'b':beta, 'rc': release candidate, 'r': release, 's':stable }
#define JSONXX_MAJOR    "0"
#define JSONXX_MINOR    "22"
#define JSONXX_EXTRA    "a"
#define JSONXX_VERSION  JSONXX_MAJOR "." JSONXX_MINOR "-" JSONXX_EXTRA
#define JSONXX_XML_TAG  "<!-- generated by jsonxx " JSONXX_VERSION " -->"

#if __cplusplus > 199711L
#define JSONXX_COMPILER_HAS_CXX11 1
#elif defined(_MSC_VER) && _MSC_VER > 1700
#define JSONXX_COMPILER_HAS_CXX11 1
#else
#define JSONXX_COMPILER_HAS_CXX11 0
#endif

#ifdef _MSC_VER
// disable the C4127 warning if using VC, see http://stackoverflow.com/a/12042515
#define JSONXX_ASSERT(...) \
  do { \
    __pragma(warning(push)) __pragma(warning(disable:4127)) \
    if( jsonxx::Assertions ) \
    __pragma(warning(pop)) \
      jsonxx::assertion(__FILE__,__LINE__,#__VA_ARGS__,bool(__VA_ARGS__)); \
  __pragma(warning(push)) __pragma(warning(disable:4127)) \
  } while(0) \
  __pragma(warning(pop))
#else
#define JSONXX_ASSERT(...) do { if( jsonxx::Assertions ) \
  jsonxx::assertion(__FILE__,__LINE__,#__VA_ARGS__,bool(__VA_ARGS__)); } while(0)
#endif

namespace jsonxx {

// FIXME(hjiang): Those should really be dynamic.
// Settings
enum Settings {
  // constants
  Enabled = true,
  Disabled = false,
  Permissive = true,
  Strict = false,
  // values
  Parser = Permissive,  // permissive or strict parsing
  UnquotedKeys = Disabled, // support of unquoted keys
  Assertions = Enabled  // enabled or disabled assertions (these asserts work both in DEBUG and RELEASE builds)
};

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4127)
#endif
inline bool parser_is_strict() { return Parser == Strict; }
inline bool parser_is_permissive() { return Parser == Permissive; }
inline bool unquoted_keys_are_enabled() { return UnquotedKeys == Enabled; }
#ifdef _MSC_VER
#pragma warning(pop)
#endif

// Constants for .write() and .xml() methods
enum Format {
  JSON      = 0,     // JSON output
  JSONx     = 1,     // XML output, JSONx  format. see http://goo.gl/I3cxs
  JXML      = 2,     // XML output, JXML   format. see https://github.com/r-lyeh/JXML
  JXMLex    = 3,     // XML output, JXMLex format. see https://github.com/r-lyeh/JXMLex
  TaggedXML = 4      // XML output, tagged XML format. see https://github.com/hjiang/jsonxx/issues/12
};

// Types
typedef long double Number;
typedef bool Boolean;
typedef std::string String;
struct Null {};
class Value;
class Object;
class Array;

// Identity meta-function
template <typename T>
struct identity {
  typedef T type;
};

// Tools
bool validate( const std::string &input );
bool validate( std::istream &input );
std::string reformat( const std::string &input );
std::string reformat( std::istream &input );
std::string xml( const std::string &input, unsigned format = JSONx );
std::string xml( std::istream &input, unsigned format = JSONx );

// Detail
void assertion( const char *file, int line, const char *expression, bool result );

// A JSON Object
class Object {
 public:
  Object();
  ~Object();

  template <typename T>
  bool has(const std::string& key) const;

  // Always call has<>() first. If the key doesn't exist, consider
  // the behavior undefined.
  template <typename T>
  T& get(const std::string& key);
  template <typename T>
  const T& get(const std::string& key) const;

  template <typename T>
  const T& get(const std::string& key, const typename identity<T>::type& default_value) const;

  size_t size() const;
  bool empty() const;

  const std::map<std::string, Value*>& kv_map() const;
  std::string json() const;
  std::string xml( unsigned format = JSONx, const std::string &header = std::string(), const std::string &attrib = std::string() ) const;
  std::string write( unsigned format ) const;

  void reset();
  bool parse(std::istream &input);
  bool parse(const std::string &input);
  typedef std::map<std::string, Value*> container;
  void import( const Object &other );
  void import( const std::string &key, const Value &value );
  Object &operator<<(const Value &value);
  Object &operator<<(const Object &value);
  Object &operator=(const Object &value);
  Object(const Object &other);
  Object(const std::string &key, const Value &value);
  template<size_t N>
  Object(const char (&key)[N], const Value &value) {
    import(key,value);
  }
  template<typename T>
  Object &operator<<(const T &value);

 protected:
  static bool parse(std::istream& input, Object& object);
  container value_map_;
  std::string odd;
};

class Array {
 public:
  Array();
  ~Array();

  size_t size() const;
  bool empty() const;

  template <typename T>
  bool has(unsigned int i) const;

  template <typename T>
  T& get(unsigned int i);
  template <typename T>
  const T& get(unsigned int i) const;

  template <typename T>
  const T& get(unsigned int i, const typename identity<T>::type& default_value) const;

  const std::vector<Value*>& values() const {
    return values_;
  }
  std::string json() const;
  std::string xml( unsigned format = JSONx, const std::string &header = std::string(), const std::string &attrib = std::string() ) const;

  std::string write( unsigned format ) const { return format == JSON ? json() : xml(format); }
  void reset();
  bool parse(std::istream &input);
  bool parse(const std::string &input);
  typedef std::vector<Value*> container;
  void append(const Array &other);
  void append(const Value &value) { import(value); }
  void import(const Array &other);
  void import(const Value &value);
  Array &operator<<(const Array &other);
  Array &operator<<(const Value &value);
  Array &operator=(const Array &other);
  Array &operator=(const Value &value);
  Array(const Array &other);
  Array(const Value &value);
 protected:
  static bool parse(std::istream& input, Array& array);
  container values_;
};

// A value could be a number, an array, a string, an object, a
// boolean, or null
class Value {
 public:

  Value();
  ~Value() { reset(); }
  void reset();

  template<typename T>
  void import( const T & ) {
    reset();
    type_ = INVALID_;
    // debug
    // std::cout << "[WARN] No support for " << typeid(t).name() << std::endl;
  }
  void import( const bool &b ) {
    reset();
    type_ = BOOL_;
    bool_value_ = b;
  }
#define $number(TYPE) \
  void import( const TYPE &n ) { \
    reset(); \
    type_ = NUMBER_; \
    number_value_ = static_cast<long double>(n); \
  }
  $number( char )
  $number( int )
  $number( long )
  $number( long long )
  $number( unsigned char )
  $number( unsigned int )
  $number( unsigned long )
  $number( unsigned long long )
  $number( float )
  $number( double )
  $number( long double )
#undef $number
#if JSONXX_COMPILER_HAS_CXX11 > 0
  void import( const std::nullptr_t & ) {
    reset();
    type_ = NULL_;
  }
#endif
  void import( const Null & ) {
    reset();
    type_ = NULL_;
  }
  void import( const String &s ) {
    reset();
    type_ = STRING_;
    *( string_value_ = new String() ) = s;
  }
  void import( const char* s ) {
    reset();
    type_ = STRING_;
    *( string_value_ = new String() ) = s;
  }
  void import( const Array &a ) {
    reset();
    type_ = ARRAY_;
    *( array_value_ = new Array() ) = a;
  }
  void import( const Object &o ) {
    reset();
    type_ = OBJECT_;
    *( object_value_ = new Object() ) = o;
  }
  void import( const Value &other ) {
    if (this != &other)
    switch (other.type_) {
      case NULL_:
        import( Null() );
        break;
      case BOOL_:
        import( other.bool_value_ );
        break;
      case NUMBER_:
        import( other.number_value_ );
        break;
      case STRING_:
        import( *other.string_value_ );
        break;
      case ARRAY_:
        import( *other.array_value_ );
        break;
      case OBJECT_:
        import( *other.object_value_ );
        break;
      case INVALID_:
        type_ = INVALID_;
        break;
      default:
        JSONXX_ASSERT( !"not implemented" );
    }
  }
  template<typename T>
  Value &operator <<( const T &t ) {
    import(t);
    return *this;
  }
  template<typename T>
  Value &operator =( const T &t ) {
    reset();
    import(t);
    return *this;
  }
  Value(const Value &other);
  template<typename T>
  Value( const T&t ) : type_(INVALID_) { import(t); }
  template<size_t N>
  Value( const char (&t)[N] ) : type_(INVALID_) { import( std::string(t) ); }

  bool parse(std::istream &input);
  bool parse(const std::string &input);

  template<typename T>
  bool is() const;
  template<typename T>
  T& get();
  template<typename T>
  const T& get() const;

  bool empty() const;

 public:
  enum {
    NUMBER_,
    STRING_,
    BOOL_,
    NULL_,
    ARRAY_,
    OBJECT_,
    INVALID_
  } type_;
  union {
    Number number_value_;
    String* string_value_;
    Boolean bool_value_;
    Array* array_value_;
    Object* object_value_;
  };

protected:
  static bool parse(std::istream& input, Value& value);
};

template <typename T>
bool Array::has(unsigned int i) const {
  if (i >= size()) {
    return false;
  } else {
    Value* v = values_.at(i);
    return v->is<T>();
  }
}

template <typename T>
T& Array::get(unsigned int i) {
  JSONXX_ASSERT(i < size());
  Value* v = values_.at(i);
  return v->get<T>();
}

template <typename T>
const T& Array::get(unsigned int i) const {
  JSONXX_ASSERT(i < size());
  const Value* v = values_.at(i);
  return v->get<T>();
}

template <typename T>
const T& Array::get(unsigned int i, const typename identity<T>::type& default_value) const {
  if(has<T>(i)) {
    const Value* v = values_.at(i);
    return v->get<T>();
  } else {
    return default_value;
  }
}

template <typename T>
bool Object::has(const std::string& key) const {
  container::const_iterator it(value_map_.find(key));
  return it != value_map_.end() && it->second->is<T>();
}

template <typename T>
T& Object::get(const std::string& key) {
  JSONXX_ASSERT(has<T>(key));
  return value_map_.find(key)->second->get<T>();
}

template <typename T>
const T& Object::get(const std::string& key) const {
  JSONXX_ASSERT(has<T>(key));
  return value_map_.find(key)->second->get<T>();
}

template <typename T>
const T& Object::get(const std::string& key, const typename identity<T>::type& default_value) const {
  if (has<T>(key)) {
    return value_map_.find(key)->second->get<T>();
  } else {
    return default_value;
  }
}

template<>
inline bool Value::is<Value>() const {
    return true;
}

template<>
inline bool Value::is<Null>() const {
  return type_ == NULL_;
}

template<>
inline bool Value::is<Boolean>() const {
  return type_ == BOOL_;
}

template<>
inline bool Value::is<String>() const {
  return type_ == STRING_;
}

template<>
inline bool Value::is<Number>() const {
  return type_ == NUMBER_;
}

template<>
inline bool Value::is<Array>() const {
  return type_ == ARRAY_;
}

template<>
inline bool Value::is<Object>() const {
  return type_ == OBJECT_;
}

template<>
inline Value& Value::get<Value>() {
    return *this;
}

template<>
inline const Value& Value::get<Value>() const {
    return *this;
}

template<>
inline bool& Value::get<Boolean>() {
  JSONXX_ASSERT(is<Boolean>());
  return bool_value_;
}

template<>
inline std::string& Value::get<String>() {
  JSONXX_ASSERT(is<String>());
  return *string_value_;
}

template<>
inline Number& Value::get<Number>() {
  JSONXX_ASSERT(is<Number>());
  return number_value_;
}

template<>
inline Array& Value::get<Array>() {
  JSONXX_ASSERT(is<Array>());
  return *array_value_;
}

template<>
inline Object& Value::get<Object>() {
  JSONXX_ASSERT(is<Object>());
  return *object_value_;
}

template<>
inline const Boolean& Value::get<Boolean>() const {
  JSONXX_ASSERT(is<Boolean>());
  return bool_value_;
}

template<>
inline const String& Value::get<String>() const {
  JSONXX_ASSERT(is<String>());
  return *string_value_;
}

template<>
inline const Number& Value::get<Number>() const {
  JSONXX_ASSERT(is<Number>());
  return number_value_;
}

template<>
inline const Array& Value::get<Array>() const {
  JSONXX_ASSERT(is<Array>());
  return *array_value_;
}

template<>
inline const Object& Value::get<Object>() const {
  JSONXX_ASSERT(is<Object>());
  return *object_value_;
}

template<typename T>
inline Object &Object::operator<<(const T &value) {
  *this << Value(value);
  return *this;
}

}  // namespace jsonxx

std::ostream& operator<<(std::ostream& stream, const jsonxx::Value& v);
std::ostream& operator<<(std::ostream& stream, const jsonxx::Object& v);
std::ostream& operator<<(std::ostream& stream, const jsonxx::Array& v);

jsonxx.cc
// -*- mode: c++; c-basic-offset: 4; -*-

// Author: Hong Jiang <[email protected]>
// Contributors:
//   Sean Middleditch <[email protected]>
//   rlyeh <https://github.com/r-lyeh>

#include "jsonxx.h"

#include <cctype>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <vector>
#include <limits>
#include <mutex>

// Snippet that creates an assertion function that works both in DEBUG & RELEASE mode.
// JSONXX_ASSERT(...) macro will redirect to this. assert() macro is kept untouched.
#if defined(NDEBUG) || defined(_NDEBUG)
#   define JSONXX_REENABLE_NDEBUG
#   undef  NDEBUG
#   undef _NDEBUG
#endif
#include <stdio.h>
#include <cassert>
void jsonxx::assertion( const char *file, int line, const char *expression, bool result ) {
    if( !result ) {
        fprintf( stderr, "[JSONXX] expression '%s' failed at %s:%d -> ", expression, file, line );
        assert( 0 );
    }
}
#if defined(JSONXX_REENABLE_NDEBUG)
#   define  NDEBUG
#   define _NDEBUG
#endif
#include <cassert>

namespace jsonxx {

//static_assert( sizeof(unsigned long long) < sizeof(long double), "'long double' cannot hold 64bit values in this compiler :(");

bool match(const char* pattern, std::istream& input);
bool parse_array(std::istream& input, Array& array);
bool parse_bool(std::istream& input, Boolean& value);
bool parse_comment(std::istream &input);
bool parse_null(std::istream& input);
bool parse_number(std::istream& input, Number& value);
bool parse_object(std::istream& input, Object& object);
bool parse_string(std::istream& input, String& value);
bool parse_identifier(std::istream& input, String& value);
bool parse_value(std::istream& input, Value& value);

// Try to consume characters from the input stream and match the
// pattern string.
bool match(const char* pattern, std::istream& input) {
    input >> std::ws;
    const char* cur(pattern);
    char ch(0);
    while(input && !input.eof() && *cur != 0) {
        input.get(ch);
        if (ch != *cur) {
            input.putback(ch);
            if( parse_comment(input) )
                continue;
            while (cur > pattern) {
                cur--;
                input.putback(*cur);
            }
            return false;
        } else {
            cur++;
        }
    }
    return *cur == 0;
}

bool parse_string(std::istream& input, String& value) {
    char ch = '\0', delimiter = '"';
    if (!match("\"", input))  {
        if (parser_is_strict()) {
            return false;
        }
        delimiter = '\'';
        if (input.peek() != delimiter) {
            return false;
        }
        input.get(ch);
    }
    while(!input.eof() && input.good()) {
        input.get(ch);
        if (ch == delimiter) {
            break;
        }
        if (ch == '\\') {
            input.get(ch);
            switch(ch) {
                case '\\':
                case '/':
                    value.push_back(ch);
                    break;
                case 'b':
                    value.push_back('\b');
                    break;
                case 'f':
                    value.push_back('\f');
                    break;
                case 'n':
                    value.push_back('\n');
                    break;
                case 'r':
                    value.push_back('\r');
                    break;
                case 't':
                    value.push_back('\t');
                    break;
                case 'u': {
                        int i;
                        std::stringstream ss;
                        for( i = 0; (!input.eof() && input.good()) && i < 4; ++i ) {
                            input.get(ch);
                            ss << std::hex << ch;
                        }
                        if( input.good() && (ss >> i) )
                            value.push_back(static_cast<char>(i));
                    }
                    break;
                default:
                    if (ch != delimiter) {
                        value.push_back('\\');
                        value.push_back(ch);
                    } else value.push_back(ch);
                    break;
            }
        } else {
            value.push_back(ch);
        }
    }
    if (input && ch == delimiter) {
        return true;
    } else {
        return false;
    }
}

bool parse_identifier(std::istream& input, String& value) {
    input >> std::ws;

    char ch = '\0', delimiter = ':';
    bool first = true;

    while(!input.eof() && input.good()) {
        input.get(ch);

        if (ch == delimiter) {
            input.unget();
            break;
        }

        if(first) {
            if ((ch != '_' && ch != '$') &&
                    (ch < 'a' || ch > 'z') &&
                    (ch < 'A' || ch > 'Z')) {
                return false;
            }
            first = false;
        }
        if(ch == '_' || ch == '$' ||
            (ch >= 'a' && ch <= 'z') ||
            (ch >= 'A' && ch <= 'Z') ||
            (ch >= '0' && ch <= '9')) {
            value.push_back(ch);
        }
        else if(ch == '\t' || ch == ' ') {
            input >> std::ws;
        }
    }
    if (input && ch == delimiter) {
        return true;
    } else {
        return false;
    }
}

class IOStateMasker {
    public:
    explicit IOStateMasker(std::istream& input): stream(input) {
       mask = input.exceptions();
       input.exceptions(std::istream::goodbit);
    }

    ~IOStateMasker() {
        stream.exceptions(mask);
    }

    private:
    std::istream& stream;
    std::istream::iostate mask;
};

bool parse_number(std::istream& input, Number& value) {
    input >> std::ws;
    std::streampos rollback = input.tellg();
    IOStateMasker masker(input);
    input >> value;
    if (input.fail()) {
        input.clear();
        input.seekg(rollback);
        return false;
    }
    return true;
}

bool parse_bool(std::istream& input, Boolean& value) {
    if (match("true", input))  {
        value = true;
        return true;
    }
    if (match("false", input)) {
        value = false;
        return true;
    }
    return false;
}

bool parse_null(std::istream& input) {
    if (match("null", input))  {
        return true;
    }
    if (parser_is_strict()) {
        return false;
    }
    return (input.peek()==',');
}

bool parse_array(std::istream& input, Array& array) {
    return array.parse(input);
}

bool parse_object(std::istream& input, Object& object) {
    return object.parse(input);
}

bool parse_comment(std::istream &input) {
    if( parser_is_permissive() )
    if( !input.eof() && input.peek() == '/' )
    {
        char ch0(0);
        input.get(ch0);

        if( !input.eof() )
        {
            char ch1(0);
            input.get(ch1);

            if( ch0 == '/' && ch1 == '/' )
            {
                // trim chars till \r or \n
                for( char ch(0); !input.eof() && (input.peek() != '\r' && input.peek() != '\n'); )
                    input.get(ch);

                // consume spaces, tabs, \r or \n, in case no eof is found
                if( !input.eof() )
                    input >> std::ws;
                return true;
            }

            input.unget();
            input.clear();
        }

        input.unget();
        input.clear();
    }

    return false;
}

bool parse_value(std::istream& input, Value& value) {
    return value.parse(input);
}


Object::Object() : value_map_() {}

Object::~Object() {
    reset();
}

bool Object::parse(std::istream& input, Object& object) {
    object.reset();

    if (!match("{", input)) {
        return false;
    }
    if (match("}", input)) {
        return true;
    }

    do {
        std::string key;
        if (unquoted_keys_are_enabled()) {
            if (!parse_identifier(input, key)) {
                if (parser_is_permissive()) {
                    if (input.peek() == '}')
                        break;
                }
                return false;
            }
        }
        else {
            if (!parse_string(input, key)) {
                if (parser_is_permissive()) {
                    if (input.peek() == '}')
                        break;
                }
                return false;
            }
        }
        if (!match(":", input)) {
            return false;
        }
        Value* v = new Value();
        if (!parse_value(input, *v)) {
            delete v;
            break;
        }
        // TODO(hjiang): Add an option to allow duplicated keys?
        if (object.value_map_.find(key) == object.value_map_.end()) {
          object.value_map_[key] = v;
        } else {
          if (parser_is_permissive()) {
            delete object.value_map_[key];
            object.value_map_[key] = v;
          } else {
            delete v;
            return false;
          }
        }
    } while (match(",", input));


    if (!match("}", input)) {
        return false;
    }

    return true;
}

Value::Value() : type_(INVALID_) {}

void Value::reset() {
    if (type_ == STRING_) {
        delete string_value_;
        string_value_ = 0;
    }
    else if (type_ == OBJECT_) {
        delete object_value_;
        object_value_ = 0;
    }
    else if (type_ == ARRAY_) {
        delete array_value_;
        array_value_ = 0;
    }
}

bool Value::parse(std::istream& input, Value& value) {
    value.reset();

    std::string string_value;
    if (parse_string(input, string_value)) {
        value.string_value_ = new std::string();
        value.string_value_->swap(string_value);
        value.type_ = STRING_;
        return true;
    }
    if (parse_number(input, value.number_value_)) {
        value.type_ = NUMBER_;
        return true;
    }

    if (parse_bool(input, value.bool_value_)) {
        value.type_ = BOOL_;
        return true;
    }
    if (parse_null(input)) {
        value.type_ = NULL_;
        return true;
    }
    if (input.peek() == '[') {
        value.array_value_ = new Array();
        if (parse_array(input, *value.array_value_)) {
            value.type_ = ARRAY_;
            return true;
        }
        delete value.array_value_;
        value.array_value_ = 0;
    }
    value.object_value_ = new Object();
    if (parse_object(input, *value.object_value_)) {
        value.type_ = OBJECT_;
        return true;
    }
    delete value.object_value_;
    value.object_value_ = 0;
    return false;
}

Array::Array() : values_() {}

Array::~Array() {
    reset();
}

bool Array::parse(std::istream& input, Array& array) {
    array.reset();

    if (!match("[", input)) {
        return false;
    }
    if (match("]", input)) {
        return true;
    }

    do {
        Value* v = new Value();
        if (!parse_value(input, *v)) {
            delete v;
            break;
        }
        array.values_.push_back(v);
    } while (match(",", input));

    if (!match("]", input)) {
        return false;
    }
    return true;
}

static std::ostream& stream_string(std::ostream& stream,
                                   const std::string& string) {
    stream << '"';
    for (std::string::const_iterator i = string.begin(),
                 e = string.end(); i != e; ++i) {
        switch (*i) {
            case '"':
                stream << "\\\"";
                break;
            case '\\':
                stream << "\\\\";
                break;
            case '/':
                stream << "\\/";
                break;
            case '\b':
                stream << "\\b";
                break;
            case '\f':
                stream << "\\f";
                break;
            case '\n':
                stream << "\\n";
                break;
            case '\r':
                stream << "\\r";
                break;
            case '\t':
                stream << "\\t";
                break;
            default:
                if (*i < 32) {
                    stream << "\\u" << std::hex << std::setw(4) <<
                            std::setfill('0') << static_cast<int>(*i) << std::dec <<
                            std::setw(0);
                } else {
                    stream << *i;
                }
        }
    }
    stream << '"';
    return stream;
}

}  // namespace jsonxx

std::ostream& operator<<(std::ostream& stream, const jsonxx::Value& v) {
    using namespace jsonxx;
    if (v.is<Number>()) {
        return stream << v.get<Number>();
    } else if (v.is<String>()) {
        return stream_string(stream, v.get<std::string>());
    } else if (v.is<Boolean>()) {
        if (v.get<Boolean>()) {
            return stream << "true";
        } else {
            return stream << "false";
        }
    } else if (v.is<Null>()) {
        return stream << "null";
    } else if (v.is<Object>()) {
        return stream << v.get<Object>();
    } else if (v.is<Array>()){
        return stream << v.get<Array>();
    }
    // Shouldn't reach here.
    return stream;
}

std::ostream& operator<<(std::ostream& stream, const jsonxx::Array& v) {
    stream << "[";
    jsonxx::Array::container::const_iterator
        it = v.values().begin(),
        end = v.values().end();
    while (it != end) {
        stream << *(*it);
        ++it;
        if (it != end) {
            stream << ", ";
        }
    }
    return stream << "]";
}

std::ostream& operator<<(std::ostream& stream, const jsonxx::Object& v) {
    stream << "{";
    jsonxx::Object::container::const_iterator
        it = v.kv_map().begin(),
        end = v.kv_map().end();
    while (it != end) {
        jsonxx::stream_string(stream, it->first);
        stream << ": " << *(it->second);
        ++it;
        if (it != end) {
            stream << ", ";
        }
    }
    return stream << "}";
}


namespace jsonxx {
namespace {

typedef unsigned char byte;

//template<bool quote>
std::string escape_string( const std::string &input, const bool quote = false ) {
    static std::string map[256], *once = 0;
    static std::mutex mutex;
    if( !once ) {
        // The problem here is that, once is initializing at the end of job, but if multithreaded application is starting this for the first time
        // it will jump into this part with several threads and cause double free or corruptions.
        // To prevent it, it is required to have mutex, to lock other threads while only one of them is constructing the static map.
        mutex.lock();
        if (!once) {
            // base
            for( int i = 0; i < 256; ++i ) {
                map[ i ] = std::string() + char(i);
            }
            // non-printable
            for( int i = 0; i < 32; ++i ) {
                std::stringstream str;
                str << "\\u" << std::hex << std::setw(4) << std::setfill('0') << i;
                map[ i ] = str.str();
            }
            // exceptions
            map[ byte('"') ] = "\\\"";
            map[ byte('\\') ] = "\\\\";
            map[ byte('/') ] = "\\/";
            map[ byte('\b') ] = "\\b";
            map[ byte('\f') ] = "\\f";
            map[ byte('\n') ] = "\\n";
            map[ byte('\r') ] = "\\r";
            map[ byte('\t') ] = "\\t";

            once = map;
        }
        mutex.unlock();
    }
    std::string output;
    output.reserve( input.size() * 2 + 2 ); // worst scenario
    if( quote ) output += '"';
    for( std::string::const_iterator it = input.begin(), end = input.end(); it != end; ++it )
        output += map[ byte(*it) ];
    if( quote ) output += '"';
    return output;
}


namespace json {

    std::string remove_last_comma( const std::string &_input ) {
        std::string input( _input );
        size_t size = input.size();
        if( size > 2 )
            if( input[ size - 2 ] == ',' )
                input[ size - 2 ] = ' ';
        return input;
    }

    std::string tag( unsigned format, unsigned depth, const std::string &name, const jsonxx::Value &t) {
        std::stringstream ss;
        const std::string tab(depth, '\t');

        if( !name.empty() )
            ss << tab << '\"' << escape_string( name ) << '\"' << ':' << ' ';
        else
            ss << tab;

        switch( t.type_ )
        {
            default:
            case jsonxx::Value::NULL_:
                ss << "null";
                return ss.str() + ",\n";

            case jsonxx::Value::BOOL_:
                ss << ( t.bool_value_ ? "true" : "false" );
                return ss.str() + ",\n";

            case jsonxx::Value::ARRAY_:
                ss << "[\n";
                for(Array::container::const_iterator it = t.array_value_->values().begin(),
                    end = t.array_value_->values().end(); it != end; ++it )
                  ss << tag( format, depth+1, std::string(), **it );
                return remove_last_comma( ss.str() ) + tab + "]" ",\n";

            case jsonxx::Value::STRING_:
                ss << '\"' << escape_string( *t.string_value_ ) << '\"';
                return ss.str() + ",\n";

            case jsonxx::Value::OBJECT_:
                ss << "{\n";
                for(Object::container::const_iterator it=t.object_value_->kv_map().begin(),
                    end = t.object_value_->kv_map().end(); it != end ; ++it)
                  ss << tag( format, depth+1, it->first, *it->second );
                return remove_last_comma( ss.str() ) + tab + "}" ",\n";

            case jsonxx::Value::NUMBER_:
                // max precision
                ss << std::setprecision(std::numeric_limits<long double>::digits10 + 1);
                ss << t.number_value_;
                return ss.str() + ",\n";
        }
    }
} // namespace jsonxx::anon::json

namespace xml {

std::string escape_attrib( const std::string &input ) {
    static std::string map[256], *once = 0;
    if( !once ) {
        for( int i = 0; i < 256; ++i )
            map[ i ] = "_";
        for( int i = int('a'); i <= int('z'); ++i )
            map[ i ] = std::string() + char(i);
        for( int i = int('A'); i <= int('Z'); ++i )
            map[ i ] = std::string() + char(i);
        for( int i = int('0'); i <= int('9'); ++i )
            map[ i ] = std::string() + char(i);
        once = map;
    }
    std::string output;
    output.reserve( input.size() ); // worst scenario
    for( std::string::const_iterator it = input.begin(), end = input.end(); it != end; ++it )
        output += map[ byte(*it) ];
    return output;
}

std::string escape_tag( const std::string &input, unsigned format ) {
    static std::string map[256], *once = 0;
    if( !once ) {
        for( int i = 0; i < 256; ++i )
            map[ i ] = std::string() + char(i);
        map[ byte('<') ] = "&lt;";
        map[ byte('>') ] = "&gt;";

        switch( format )
        {
            default:
                break;

            case jsonxx::JXML:
            case jsonxx::JXMLex:
            case jsonxx::JSONx:
            case jsonxx::TaggedXML:
                map[ byte('&') ] = "&amp;";
                break;
        }

        once = map;
    }
    std::string output;
    output.reserve( input.size() * 5 ); // worst scenario
    for( std::string::const_iterator it = input.begin(), end = input.end(); it != end; ++it )
        output += map[ byte(*it) ];
    return output;
}

std::string open_tag( unsigned format, char type, const std::string &name, const std::string &attr = std::string(), const std::string &text = std::string() ) {
    std::string tagname;
    switch( format )
    {
        default:
            return std::string();

        case jsonxx::JXML:
            if( name.empty() )
                tagname = std::string("j son=\"") + type + '\"';
            else
                tagname = std::string("j son=\"") + type + ':' + escape_string(name) + '\"';
            break;

        case jsonxx::JXMLex:
            if( name.empty() )
                tagname = std::string("j son=\"") + type + '\"';
            else
                tagname = std::string("j son=\"") + type + ':' + escape_string(name) + "\" " + escape_attrib(name) + "=\"" + escape_string(text) + "\"";
            break;

        case jsonxx::JSONx:
            if( !name.empty() )
                tagname = std::string(" name=\"") + escape_string(name) + "\"";
            switch( type ) {
                default:
                case '0': tagname = "json:null" + tagname; break;
                case 'b': tagname = "json:boolean" + tagname; break;
                case 'a': tagname = "json:array" + tagname; break;
                case 's': tagname = "json:string" + tagname; break;
                case 'o': tagname = "json:object" + tagname; break;
                case 'n': tagname = "json:number" + tagname; break;
            }
            break;

        case jsonxx::TaggedXML: // @TheMadButcher
            if( !name.empty() )
                tagname = escape_attrib(name);
            else
                tagname = "JsonItem";
            switch( type ) {
                default:
                case '0': tagname += " type=\"json:null\""; break;
                case 'b': tagname += " type=\"json:boolean\""; break;
                case 'a': tagname += " type=\"json:array\""; break;
                case 's': tagname += " type=\"json:string\""; break;
                case 'o': tagname += " type=\"json:object\""; break;
                case 'n': tagname += " type=\"json:number\""; break;
            }

            if( !name.empty() )
                tagname += std::string(" name=\"") + escape_string(name) + "\"";

            break;
    }

    return std::string("<") + tagname + attr + ">";
}

std::string close_tag( unsigned format, char type, const std::string &name ) {
    switch( format )
    {
        default:
            return std::string();

        case jsonxx::JXML:
        case jsonxx::JXMLex:
            return "</j>";

        case jsonxx::JSONx:
            switch( type ) {
                default:
                case '0': return "</json:null>";
                case 'b': return "</json:boolean>";
                case 'a': return "</json:array>";
                case 'o': return "</json:object>";
                case 's': return "</json:string>";
                case 'n': return "</json:number>";
            }
            break;

        case jsonxx::TaggedXML: // @TheMadButcher
            if( !name.empty() )
                return "</"+escape_attrib(name)+">";
            else
                return "</JsonItem>";
    }
}

std::string tag( unsigned format, unsigned depth, const std::string &name, const jsonxx::Value &t, const std::string &attr = std::string() ) {
    std::stringstream ss;
    const std::string tab(depth, '\t');

    switch( t.type_ )
    {
        default:
        case jsonxx::Value::NULL_:
            return tab + open_tag( format, '0', name, " /" ) + '\n';

        case jsonxx::Value::BOOL_:
            ss << ( t.bool_value_ ? "true" : "false" );
            return tab + open_tag( format, 'b', name, std::string(), format == jsonxx::JXMLex ? ss.str() : std::string() )
                       + ss.str()
                       + close_tag( format, 'b', name ) + '\n';

        case jsonxx::Value::ARRAY_:
            for(Array::container::const_iterator it = t.array_value_->values().begin(),
                end = t.array_value_->values().end(); it != end; ++it )
              ss << tag( format, depth+1, std::string(), **it );
            return tab + open_tag( format, 'a', name, attr ) + '\n'
                       + ss.str()
                 + tab + close_tag( format, 'a', name ) + '\n';

        case jsonxx::Value::STRING_:
            ss << escape_tag( *t.string_value_, format );
            return tab + open_tag( format, 's', name, std::string(), format == jsonxx::JXMLex ? ss.str() : std::string() )
                       + ss.str()
                       + close_tag( format, 's', name ) + '\n';

        case jsonxx::Value::OBJECT_:
            for(Object::container::const_iterator it=t.object_value_->kv_map().begin(),
                end = t.object_value_->kv_map().end(); it != end ; ++it)
              ss << tag( format, depth+1, it->first, *it->second );
            return tab + open_tag( format, 'o', name, attr ) + '\n'
                       + ss.str()
                 + tab + close_tag( format, 'o', name ) + '\n';

        case jsonxx::Value::NUMBER_:
            // max precision
            ss << std::setprecision(std::numeric_limits<long double>::digits10 + 1);
            ss << t.number_value_;
            return tab + open_tag( format, 'n', name, std::string(), format == jsonxx::JXMLex ? ss.str() : std::string() )
                       + ss.str()
                       + close_tag( format, 'n', name ) + '\n';
    }
}

// order here matches jsonxx::Format enum
const char *defheader[] = {
    "",

    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
         JSONXX_XML_TAG "\n",

    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
         JSONXX_XML_TAG "\n",

    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
         JSONXX_XML_TAG "\n",

    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
         JSONXX_XML_TAG "\n"
};

// order here matches jsonxx::Format enum
const char *defrootattrib[] = {
    "",

    " xsi:schemaLocation=\"http://www.datapower.com/schemas/json jsonx.xsd\""
        " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
        " xmlns:json=\"http://www.ibm.com/xmlns/prod/2009/jsonx\"",

    "",

    "",

    ""
};

} // namespace jsonxx::anon::xml

} // namespace jsonxx::anon

std::string Object::json() const {
    using namespace json;

    jsonxx::Value v;
    v.object_value_ = const_cast<jsonxx::Object*>(this);
    v.type_ = jsonxx::Value::OBJECT_;

    std::string result = tag( jsonxx::JSON, 0, std::string(), v );

    v.object_value_ = 0;
    return remove_last_comma( result );
}

std::string Object::xml( unsigned format, const std::string &header, const std::string &attrib ) const {
    using namespace xml;
    JSONXX_ASSERT( format == jsonxx::JSONx || format == jsonxx::JXML || format == jsonxx::JXMLex || format == jsonxx::TaggedXML );

    jsonxx::Value v;
    v.object_value_ = const_cast<jsonxx::Object*>(this);
    v.type_ = jsonxx::Value::OBJECT_;

    std::string result = tag( format, 0, std::string(), v, attrib.empty() ? std::string(defrootattrib[format]) : attrib );

    v.object_value_ = 0;
    return ( header.empty() ? std::string(defheader[format]) : header ) + result;
}

std::string Array::json() const {
    using namespace json;

    jsonxx::Value v;
    v.array_value_ = const_cast<jsonxx::Array*>(this);
    v.type_ = jsonxx::Value::ARRAY_;

    std::string result = tag( jsonxx::JSON, 0, std::string(), v );

    v.array_value_ = 0;
    return remove_last_comma( result );
}

std::string Array::xml( unsigned format, const std::string &header, const std::string &attrib ) const {
    using namespace xml;
    JSONXX_ASSERT( format == jsonxx::JSONx || format == jsonxx::JXML || format == jsonxx::JXMLex || format == jsonxx::TaggedXML );

    jsonxx::Value v;
    v.array_value_ = const_cast<jsonxx::Array*>(this);
    v.type_ = jsonxx::Value::ARRAY_;

    std::string result = tag( format, 0, std::string(), v, attrib.empty() ? std::string(defrootattrib[format]) : attrib );

    v.array_value_ = 0;
    return ( header.empty() ? std::string(defheader[format]) : header ) + result;
}

bool validate( std::istream &input ) {

    // trim non-printable chars
    for( char ch(0); !input.eof() && input.peek() <= 32; )
        input.get(ch);

    // validate json
    if( input.peek() == '{' )
    {
        jsonxx::Object o;
        if( parse_object( input, o ) )
            return true;
    }
    else
    if( input.peek() == '[' )
    {
        jsonxx::Array a;
        if( parse_array( input, a ) )
            return true;
    }

    // bad json input
    return false;
}

bool validate( const std::string &input ) {
    std::istringstream is( input );
    return jsonxx::validate( is );
}

std::string reformat( std::istream &input ) {

    // trim non-printable chars
    for( char ch(0); !input.eof() && input.peek() <= 32; )
        input.get(ch);

    // validate json
    if( input.peek() == '{' )
    {
        jsonxx::Object o;
        if( parse_object( input, o ) )
            return o.json();
    }
    else
    if( input.peek() == '[' )
    {
        jsonxx::Array a;
        if( parse_array( input, a ) )
            return a.json();
    }

    // bad json input
    return std::string();
}

std::string reformat( const std::string &input ) {
    std::istringstream is( input );
    return jsonxx::reformat( is );
}

std::string xml( std::istream &input, unsigned format ) {
    using namespace xml;
    JSONXX_ASSERT( format == jsonxx::JSONx || format == jsonxx::JXML || format == jsonxx::JXMLex || format == jsonxx::TaggedXML );

    // trim non-printable chars
    for( char ch(0); !input.eof() && input.peek() <= 32; )
        input.get(ch);

    // validate json, then transform
    if( input.peek() == '{' )
    {
        jsonxx::Object o;
        if( parse_object( input, o ) )
            return o.xml(format);
    }
    else
    if( input.peek() == '[' )
    {
        jsonxx::Array a;
        if( parse_array( input, a ) )
            return a.xml(format);
    }

    // bad json, return empty xml
    return defheader[format];
}

std::string xml( const std::string &input, unsigned format ) {
    std::istringstream is( input );
    return jsonxx::xml( is, format );
}


Object::Object(const Object &other) {
  import(other);
}
Object::Object(const std::string &key, const Value &value) {
  import(key,value);
}
void Object::import( const Object &other ) {
  odd.clear();
  if (this != &other) {
    // default
    container::const_iterator
        it = other.value_map_.begin(),
        end = other.value_map_.end();
    for (/**/ ; it != end ; ++it) {
      container::iterator found = value_map_.find(it->first);
      if( found != value_map_.end() ) {
        delete found->second;
      }
      value_map_[ it->first ] = new Value( *it->second );
    }
  } else {
    // recursion is supported here
    import( Object(*this) );
  }
}
void Object::import( const std::string &key, const Value &value ) {
  odd.clear();
  container::iterator found = value_map_.find(key);
  if( found != value_map_.end() ) {
    delete found->second;
  }
  value_map_[ key ] = new Value( value );
}
Object &Object::operator=(const Object &other) {
  odd.clear();
  if (this != &other) {
    reset();
    import(other);
  }
  return *this;
}
Object &Object::operator<<(const Value &value) {
  if (odd.empty()) {
    odd = value.get<String>();
  } else {
    import( Object(odd, value) );
    odd.clear();
  }
  return *this;
}
Object &Object::operator<<(const Object &value) {
  import( std::string(odd),value);
  odd.clear();
  return *this;
}
size_t Object::size() const {
  return value_map_.size();
}
bool Object::empty() const {
  return value_map_.size() == 0;
}
const std::map<std::string, Value*> &Object::kv_map() const {
  return value_map_;
}
std::string Object::write( unsigned format ) const {
  return format == JSON ? json() : xml(format);
}
void Object::reset() {
  container::iterator i;
  for (i = value_map_.begin(); i != value_map_.end(); ++i) {
    delete i->second;
  }
  value_map_.clear();
}
bool Object::parse(std::istream &input) {
  return parse(input,*this);
}
bool Object::parse(const std::string &input) {
  std::istringstream is( input );
  return parse(is,*this);
}


Array::Array(const Array &other) {
  import(other);
}
Array::Array(const Value &value) {
  import(value);
}
void Array::append(const Array &other) {
    if (this != &other) {
        values_.push_back( new Value(other) );
    } else {
        append( Array(*this) );
    }
}
void Array::import(const Array &other) {
  if (this != &other) {
    // default
    container::const_iterator
        it = other.values_.begin(),
        end = other.values_.end();
    for (/**/ ; it != end; ++it) {
      values_.push_back( new Value(**it) );
    }
  } else {
    // recursion is supported here
    import( Array(*this) );
  }
}
void Array::import(const Value &value) {
  values_.push_back( new Value(value) );
}
size_t Array::size() const {
  return values_.size();
}
bool Array::empty() const {
  return values_.size() == 0;
}
void Array::reset() {
  for (container::iterator i = values_.begin(); i != values_.end(); ++i) {
    delete *i;
  }
  values_.clear();
}
bool Array::parse(std::istream &input) {
  return parse(input,*this);
}
bool Array::parse(const std::string &input) {
  std::istringstream is(input);
  return parse(is,*this);
}
Array &Array::operator<<(const Array &other) {
  import(other);
  return *this;
}
Array &Array::operator<<(const Value &value) {
  import(value);
  return *this;
}
Array &Array::operator=(const Array &other) {
  if( this != &other ) {
    reset();
    import(other);
  }
  return *this;
}
Array &Array::operator=(const Value &value) {
  reset();
  import(value);
  return *this;
}

Value::Value(const Value &other) : type_(INVALID_) {
  import( other );
}
bool Value::empty() const {
  if( type_ == INVALID_ ) return true;
  if( type_ == STRING_ && string_value_ == 0 ) return true;
  if( type_ == ARRAY_ && array_value_ == 0 ) return true;
  if( type_ == OBJECT_ && object_value_ == 0 ) return true;
  return false;
}
bool Value::parse(std::istream &input) {
  return parse(input,*this);
}
bool Value::parse(const std::string &input) {
  std::istringstream is( input );
  return parse(is,*this);
}

}  // namespace jsonxx

後面的json解析來自於github吧,雖然qt也有json解析,但是哪有我直接從以前工程複製程式碼來得爽快。

也許還有完善的地方吧,反正先用用再說了。