4
\$\begingroup\$

JsonStatus

(as in status file)

Goal

A simple and fast JSON writer in C++ (JSON output only). When you need something so small that you're not sure you even need a JSON library and you don't feel like adding lots of code into your project but you also want something to take care of the commas, colons and braces for you.

Properties

Minimum verification and assumes that data is added in-order.

Doesn't protect against duplicate keys and values can't be modified, only written once.

Writes directly to a file.

Code

Usage example

    JsonStatus jst;
    jst.setFileName("aaa.json");
    jst.initArray();
    jst.pushVal(1);
    jst.pushVal(2);
    jst.objectInArray();
    jst.pushKeyArray("arr");
    jst.pushVal(3);
    jst.pushVal(4);
    jst.close();  // pops all nested

    //Output: [1, 2, {"arr" : [3, 4]}]

    jst.setFileName("bbb.json");
    jst.initObject();
    jst.pushKeyArray("arr");
    jst.pushVal(1);
    jst.pushVal(2);
    jst.popNested(); // End arr
    jst.pushKeyVal("keyA", 3);
    jst.pushKeyObject("othertypes");
    jst.pushKeyVal("keyB", 4.001);
    jst.pushKeyObject("strings");
    jst.pushKeyVal("keyC", "5");
    jst.pushKeyVal("keyD", std::string("5") + "+1");
    jst.popNested(); // End strings
    jst.pushKeyVal("keyE", 'E');
    jst.close();  // pops all nested

    //Output: {"arr" : [1, 2], "keyA" : 3, "othertypes" : {"keyB" : 4.001, "strings" : {"keyC" : "5", "keyD" : "5+1"}, "keyE" : "E"}}

Header file

#ifndef JSONSTATUS_H_
#define JSONSTATUS_H_

#include <string>
#include <fstream>
#include <stack>
#include <stdexcept>

class JsonStatus
{
public:
    void setFileName(const std::string& fileName);
    void initObject();
    void initArray();
    void close();

    void pushKeyVal(const std::string& key, const std::string& val);
    void pushKeyVal(const std::string& key, const char* val);
    void pushKeyVal(const std::string& key, char val);

    template <class T>
    void pushKeyVal(const std::string& key, const T& val)
    {
        pushKey(key);
        jsonString << val;
    }

    // adding elements to a JSON array
    void pushVal(const std::string& val);
    void pushVal(const char* val);
    void pushVal(char val);

    template <class T>
    void pushVal(const T& val)
    {
        arrayState();
        jsonString << val;
    }

    // Nested arrays and objects
    void objectInArray();
    void pushKeyArray(const std::string&);
    void pushKeyObject(const std::string&);
    void popNested();
    void popAllNested();

private:
    void pushKey(const std::string& key);
    void arrayState();
    void writeStringInQuotes(const char* str);
    void writeCharInQuotes(char c);

    enum class Nested
    {
        Object,
        Array
    };

    std::string fileName;
    std::ofstream jsonString;
    struct level
    {
        Nested nested;
        int count = 0;
    };

    std::stack<level> currentLevel;
};

#endif /* JSONSTATUS_H_ */

Implementation file


#include "JsonStatus.h"

void JsonStatus::setFileName(const std::string& fileNameArg)
{
    fileName = fileNameArg;
}

void JsonStatus::pushKeyVal(const std::string &key, const std::string &val)
{
    pushKeyVal(key, val.c_str());
}

void JsonStatus::pushKeyVal(const std::string &key, const char *val)
{
    pushKey(key);
    writeStringInQuotes(val);
}

void JsonStatus::pushKeyVal(const std::string& key, char val)
{
    pushKey(key);
    writeCharInQuotes(val);
}

void JsonStatus::pushVal(const std::string& val)
{
    pushVal(val.c_str());
}

void JsonStatus::pushVal(const char* val)
{
    arrayState();
    writeStringInQuotes(val);
}

void JsonStatus::pushVal(char val)
{
    arrayState();
    writeCharInQuotes(val);
}

void JsonStatus::objectInArray()
{
    arrayState();
    jsonString << '{';
    currentLevel.push(level{ Nested::Object });
}

void JsonStatus::pushKey(const std::string &key)
{
    if (currentLevel.empty())
        throw std::runtime_error("JsonStatus was not initialized");
    if (currentLevel.top().nested != Nested::Object)
        throw std::runtime_error("Pushed a key to an array");
    if (currentLevel.top().count)
        jsonString << ", ";
    writeStringInQuotes(key.c_str());
    jsonString << " : ";
    ++currentLevel.top().count;
}

void JsonStatus::arrayState()
{
    if (currentLevel.empty())
        throw std::runtime_error("JsonStatus was not initialized");
    if (currentLevel.top().nested != Nested::Array)
        throw std::runtime_error("Pushed a value without key to an object");
    if (currentLevel.top().count)
        jsonString << ", ";
    ++currentLevel.top().count;
}


void JsonStatus::pushKeyArray(const std::string &key)
{
    pushKey(key);
    jsonString << '[';
    currentLevel.push(level{ Nested::Array });
}

void JsonStatus::pushKeyObject(const std::string &key)
{
    pushKey(key);
    jsonString << '{';
    currentLevel.push(level{ Nested::Object });
}

void JsonStatus::popNested()
{
    if (currentLevel.empty())
        throw std::runtime_error("JsonStatus was not initialized");
    if (currentLevel.top().nested == Nested::Array)
        jsonString << ']';
    else if (currentLevel.top().nested == Nested::Object)
        jsonString << '}';
    currentLevel.pop();
}

void JsonStatus::initObject()
{
    jsonString.open(fileName);
    if (jsonString.is_open() == false || jsonString.good() == false)
        throw std::runtime_error("Could not open JSON path for writing");

    while (!currentLevel.empty())
        currentLevel.pop();

    jsonString << '{';
    currentLevel.push(level{ Nested::Object });
}

void JsonStatus::initArray()
{
    jsonString.open(fileName);
    if (jsonString.is_open() == false || jsonString.good() == false)
        throw std::runtime_error("Could not open JSON path for writing");

    while (!currentLevel.empty())
        currentLevel.pop();

    jsonString << '[';
    currentLevel.push(level{ Nested::Array });
}

void JsonStatus::popAllNested()
{
    while (!currentLevel.empty())
        popNested();
}

void JsonStatus::close()
{
    popAllNested();
    jsonString.close();
}

void JsonStatus::writeStringInQuotes(const char* str)
{
    // if you're feeling fancy, escape special characters, not doing it for now.
    jsonString << '"';
    jsonString << str;
    jsonString << '"';
}

void JsonStatus::writeCharInQuotes(char c)
{
    char str[] = { c,'\0' };
    writeStringInQuotes(str);
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.