Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

ZetJsonCpp - Deserialize/Serialize from/to JSON Strings or Files

4.94/5 (19 votes)
6 Apr 2021MIT5 min read 68.2K   213  
A library to parse and bind Json string or files to C structure
This article will show how to deserialize from JSON string or JSON file into C structure in one single line with ZetJsonCpp. Also, it will show how to perform some operations and serialize reflecting the changes.

It can also be downloaded from Github.

Introduction

Nowadays, JSON structure has become the most used in web apps to interchange data. This is because JSON is the simplest and most human readable format, so it is more friendly to use. This article aims to explain how to deserialize/serialize from/to json strings or files using zetjsoncpp library in C++.

1. Deserialization

To serialize JSON variable is done using zetjsoncpp::deserialize. Zet JsonCpp supports deserialization of the following types:

  • Boolean
  • Vector of booleans
  • Map of booleans
  • Number
  • Vector of numbers
  • Map of numbers
  • String
  • Vector of strings
  • Map of strings
  • Object
  • Vector of objects
  • Map of objects

2.1 Boolean

To deserialize a JSON boolean, it is done through JsonVarBoolean as shown below:

C++
zetjsoncpp::deserialize<zetjsoncpp::JsonVarBoolean<>>("true");

2.2 Vector of Booleans

A JSON vector of booleans could be the following:

JavaScript
[true,false,true]

To deserialize a JSON vector of booleans, it is done through JsonVarVectorBoolean as it shows below:

C++
zetjsoncpp::deserialize<zetjsoncpp::JsonVarVectorBoolean<>>(
"["
   "true"
   ",false"
   ",true"
"]");

2.3 Map of Booleans

A JSON map of booleans could be the following:

JavaScript
{
   "id1":true
  ,"id2":false
  ,"id3":true
}

To deserialize a JSON map of booleans, it is done through JsonVarMapBoolean as it shows below:

C++
zetjsoncpp::deserialize<zetjsoncpp::JsonVarMapBoolean<>>(
"{"
   "\"id1\":true"
   ",\"id2\":false"
   ",\"id3\":true"
"}");

2.4 Number

A JSON number could be the following:

  • -1
  • 2.5
  • 3.7e+2

To deserialize a JSON number, it is done through JsonVarNumber as shown below:

C++
zetjsoncpp::deserialize<zetjsoncpp::JsonVarNumber<>>("1");

2.5 Vector of Numbers

A JSON vector of numbers could be the following,

JavaScript
[1,3.7e+2,-3]

To deserialize a JSON vector of numbers, it is done through JsonVarVectorNumber as shown below:

C++
zetjsoncpp::deserialize<zetjsoncpp::JsonVarVectorNumber<>>(
"["
   "1"
   ",3.7e+2"
   ",-3"
"]");

2.6 Map of Numbers

A JSON map of numbers could be the following:

JavaScript
{ 
  "id1":1
 ,"id2":3.7e+2
 ,"id3":-3
}

To deserialize a JSON map of numbers, it is done through JsonVarMapNumber as shown below:

C++
zetjsoncpp::deserialize<zetjsoncpp::JsonVarMapNumber<>>(
"{"
   "\"id1\":1"
   ",\"id2\":3.7e+2"
   ",\"id3\":-3"
"}");

2.7 String

To deserialize a JSON string, it is done through JsonVarString as shown below:

C++
zetjsoncpp::deserialize<zetjsoncpp::JsonVarString<>>("\"my_string\"")

2.8 Vector of Strings

A JSON vector of strings could be the following:

JavaScript
["string_1","string_2","string_3"]

To deserialize a vector of strings, it is done through JsonVarVectorString as shown below:

C++
zetjsoncpp::deserialize<JsonVarVectorString<>>(
"["
    "\"string_1\""
    ",\"string_2\""
    ",\"string_3\""
"]");

2.9 Map of Strings

A JSON map of strings could be the following:

JavaScript
{ 
   "id1":"string_1"
   ,"id2":"string_2"
   ,"id3":"string_3" 
}

To deserialize a map of strings, it is done through JsonVarMapString as shown below:

C++
JsonVarMapString<> *m3=zetjsoncpp::deserialize<zetjsoncpp::JsonVarMapString<>>(
"{"
    "\"id1\":\"string_1\""
    ",\"id2\":\"string_2\""
    ",\"id3\":\"string_3\""
"}");

2.10 Object

Until now, it has seen a way to serialize primitive and structured types that are easy to understand. Now we present the method to deserialize JSON object that requires a little bit of setup. A JSON object is like a JSON map with different content on its values.

A JSON object could be the following:

JavaScript
{
  "encoding":"UTF-8"
 ,"length":1000
 ,"use_space":false
}
List 2.1

Taking the example of list 2.1, in zetjsoncpp, it defines JSON object using a structure in C as shown below:

C++
typedef struct{
    zetjsoncpp::JsonVarString<ZJ_CONST_CHAR("encoding")>    encoding;
    zetjsoncpp::JsonVarNumber<ZJ_CONST_CHAR("length")>      length;     
    zetjsoncpp::JsonVarBoolean<ZJ_CONST_CHAR("use_space")>  use_space; 
}JsonSample;
List 2.2

Note:

You may have noticed ZJ_CONST_CHAR(s). This is a trick to pass literal string through variadic templates char by char, so that templates don't accept pass literal strings (i.e., const char *) as a parameter.

And finally, to deserialize a json object, it is done through JsonVarObject passing the type of structure to deserialize it has seen in list 2.2:

C++
auto json_object=zetjsoncpp::deserialize<zetjsoncpp::JsonVarObject<JsonSample>>(
"{"
   "\"encoding\":\"UTF-8\""
   ",\"length\":1000"
   ",\"use_space\":false"         
"}");

If any variable has not been deserialized, because it does not exist in string/file, either it doesn't match json property name json with that defined in the C++ structure, it has to use isDeserialized() to check whether the variable was deserialized or not.

For example:

C++
if(json_object->encoding.isDeserialized()){

// value deserialized ok. do something... 

}
List 3.4

By default, any no deserialized variable the strings are set empty, numbers and booleans will set as 0 and false respectively.

2.11 Vector of Objects

A JSON vector of objects could be as follows:

JavaScript
[{ 
    "encoding":"UTF-8" 
    ,"length":1000 
    ,"use_space":false 
 },{   
    "encoding":"ANSII"
   ,"length":1500   
   ,"use_space":true 
}]

To deserialize a vector of objects, it is done through JsonVarVectorObject passing the type of structure to deserialize it has seen in list 2.2:

C++
zetjsoncpp::deserialize<zetjsoncpp::JsonVarVectorObject<JsonSample>>(
"[{"  
   "\"encoding\":\"UTF-8\""  
   ",\"length\":1000"   
   ",\"use_space\":false" 
  "},{"  
    "\"encoding\":\"ANSII\""  
    ",\"length\":1500"  
    ",\"use_space\":true" 
"}]");

2.12 Map of Objects

A json map of objects could be the following:

JavaScript
{
  "id1":{ 
     "encoding":"UTF-8" 
    ,"length":1000 
    ,"use_space":false
  }
  ,"id2":{
     "encoding":"ANSII"
     ,"length":1500
     ,"use_space":true
  }
}

To deserialize a map of objects, it is done through JsonVarMapObject passing the type of structure to deserialize it has seen in list 2.2:

C++
zetjsoncpp::deserialize<zetjsoncpp::JsonVarMapObject<JsonSample>>(
"{"
  "\"id1\":{"
     "\"encoding\":\"UTF-8\""  
     ",\"length\":1000"    
     ",\"use_space\":false" 
  "}"
  ",\"id2\":{"
      "\"encoding\":\"ANSII\""
      ",\"length\":1500"  
      ",\"use_space\":true" 
  "}"
"}");

2. Serialize

To serialize JSON variable, it is done using zetjsoncpp::serialize.

For example:

C++
// parse json var number
auto json_number=zetjsoncpp::deserialize<zetjsoncpp::JsonVarNumber<>>("2");

// change it by 3.5
json_number=3.5;

std::cout << zetjsoncpp::serialize(json_var); << std::enl;

// it outputs 3.5

3. Example

In this section, it will see an example to deserialize almost all types mentioned in section 2. Let's suppose it has a file called sample.json with the following content:

JavaScript
// Configuration options
{
    // Default encoding for text
    "encoding" : "UTF-8",
    "number": 3.34E-5,
    // Plug-ins loaded at start-up
    "plug-ins" : [
        "python",
        "c++",
        "ruby"
      ],
        
    // Tab indent size
    "indent" : { "length" : 3, "use_space": true },
    
    // set of languages
    "languages":[{
    
        "code" : "en",
        "general_texts": {
            "general.hello_word":"Hello world!"
            ,"general.yes":"Yes"
            ,"general.no":"No"
        }
        
    },{
        "code" : "es",
        "general_texts": {
            "general.hello_word":"Hola mundo!"
            ,"general.yes":"Si"
            ,"general.no":"No"
        }
    },{
        "code" : "zh-CN",
        "general_texts": {
            "general.hello_word":"你好词"
            ,"general.yes":"是"
            ,"general.no":"没有"
        }
    }]
    // set of interpolators
    ,"interpolations":{
        "id_1":{
             "type":"material"
            ,"channels":"rgb"
            ,"data":[
             // r    g   b   t
             //---- --- --- ----
                0.0,1.0,0.0,1000
                ,0.0,0.0,0.0,0
            ]
        },"id_2":{
             "type":"transform"
            ,"channels":"xyz"
            ,"data":[
             // x    y   z   t
             //---- --- --- ----
                0.0,1.0,0.0,1000
                ,0.0,0.0,0.0,0
            ]
        }
    }
}
List 3.1

We can associate JSON structure seen on list 3.1 with the following C structure with JSON vars:

C++
#include "zetjsoncpp.h"

using zetjsoncpp::JsonVarNumber;
using zetjsoncpp::JsonVarBoolean;
using zetjsoncpp::JsonVarString;
using zetjsoncpp::JsonVarMapString;
using zetjsoncpp::JsonVarVectorNumber;
using zetjsoncpp::JsonVarVectorString;
using zetjsoncpp::JsonVarObject;
using zetjsoncpp::JsonVarVectorObject;
using zetjsoncpp::JsonVarMapObject;

typedef struct{
    // Number length
    JsonVarNumber<ZJ_CONST_CHAR("length")>    length;

    // Boolean use_space
    JsonVarBoolean<ZJ_CONST_CHAR("use_space")> use_space;
}Ident;

typedef struct{
    // String code
    JsonVarString<ZJ_CONST_CHAR("code")>    code;

    // Map of strings general_texts
    JsonVarMapString<ZJ_CONST_CHAR("general_texts")>    general_texts;
}Language;

typedef struct{
    // String type
    JsonVarString<ZJ_CONST_CHAR("type")>    type;

    // String channels
    JsonVarString<ZJ_CONST_CHAR("channels")>    channels;

    // Vector of numbers data
    JsonVarVectorNumber<ZJ_CONST_CHAR("data")>    data;
}Interpolation;

typedef struct
{
    // String encoding
    JsonVarString<ZJ_CONST_CHAR("encoding")>    encoding;

    // Number number
    JsonVarNumber<ZJ_CONST_CHAR("number")>    number;

    // Vector of strings plug-ins
    JsonVarVectorString<ZJ_CONST_CHAR("plug-ins")>    plugins;

    // Object indent
    JsonVarObject<Ident,ZJ_CONST_CHAR("indent")>    indent;

    // Object languages
    JsonVarVectorObject<Language,ZJ_CONST_CHAR("languages")>    languages;

    // Map of objects interpolations
    JsonVarMapObject<Interpolation,ZJ_CONST_CHAR("interpolations")>    interpolations;

}SampleJson;
List 3.2

Below, you can see an example of deserialization, data manipulation of the loaded json object, and finally serialization to reflect the changes made.

C++
int main(int argc, char *argv[]){

    try{
        auto json_object=zetjsoncpp::deserialize_file<JsonVarObject<SampleJson>>("sample.json");

        // the values before modifications.
        std::cout << "---------------------------------------------------" << std::endl;
        std::cout << " Before modifications:"<< std::endl;
        std::cout << zetjsoncpp::serialize(json_object);

        // From here we can operate with loaded data in our program using c++ operators
        // put m_use_space to false...
        json_object->indent.use_space = false;

        // iterate of all plugins and replace with random strings...
        for(unsigned i = 0; i < json_object->plugins.size(); i++) {
            json_object->plugins[i] = 
                 "my_randomstring"+zetjsoncpp::zj_strutils::int_to_str(i+1);
        }

        // iterate of all interpolations and replace its data values...
        for(auto it_map = json_object->interpolations.begin(); 
            it_map != json_object->interpolations.end(); it_map++) {
            for(auto it = it_map->second->data.begin(); 
                it != it_map->second->data.end(); it++) {
                *it = rand();
            }
        }

        std::cout << "--------------------------------------------------" << std::endl;
        std::cout << " After modifications:"<< std::endl;
        std::cout << zetjsoncpp::serialize(json_object);

        // destroy json_object
        delete json_object;
  }catch(std::exception & ex){
    std::cerr << "Error:" << ex.what() << std::endl;
  }
}
List 3.3

After its execution, the output shows the serialized json before and after the changes marked in bold,

---------------------------------------------------
 Before modifications:
{
    "encoding":"UTF-8",
    "number":0.000033,
    "plug-ins":
    [
        "python","c++","ruby"
    ],
    "indent":
    {
        "length":3.000000,
        "use_space":true
    },
    "languages":
    [{
        "code":"en",
        "general_texts":
        {
            "general.hello_word":"Hello world!"
            ,"general.no":"No"
            ,"general.yes":"Yes"
        }
    },{
        "code":"es",
        "general_texts":
        {
            "general.hello_word":"Hola mundo!"
            ,"general.no":"No"
            ,"general.yes":"Si"
        }
    },{
        "code":"zh-CN",
        "general_texts":
        {
            "general.hello_word":"你好词"
            ,"general.no":"没有"
            ,"general.yes":"是"
        }
    }],
    "interpolations":
    {
        "id_1":{
            "type":"material",
            "channels":"rgb",
            "data":
            [
                0.000000,1.000000,0.000000,1000.000000,0.000000,0.000000,0.000000,0.000000
            ]
        }
        ,"id_2":{
            "type":"transform",
            "channels":"xyz",
            "data":
            [
                0.000000,1.000000,0.000000,1000.000000,0.000000,0.000000,0.000000,0.000000
            ]
        }
    }
}--------------------------------------------------
 After modifications:
{
    "encoding":"UTF-8",
    "number":0.000033,
    "plug-ins":
    [
        "my_randomstring1","my_randomstring2","my_randomstring3"
    ],
    "indent":
    {
        "length":3.000000,
        "use_space":false
    },
    "languages":
    [{
        "code":"en",
        "general_texts":
        {
            "general.hello_word":"Hello world!"
            ,"general.no":"No"
            ,"general.yes":"Yes"
        }
    },{
        "code":"es",
        "general_texts":
        {
            "general.hello_word":"Hola mundo!"
            ,"general.no":"No"
            ,"general.yes":"Si"
        }
    },{
        "code":"zh-CN",
        "general_texts":
        {
            "general.hello_word":"你好词"
            ,"general.no":"没有"
            ,"general.yes":"是"
        }
    }],
    "interpolations":
    {
        "id_1":{
            "type":"material",
            "channels":"rgb",
            "data":
            [
                41.000000,18467.000000,6334.000000,26500.000000,19169.000000,
                15724.000000,11478.000000,29358.000000
            ]
        }
        ,"id_2":{
            "type":"transform",
            "channels":"xyz",
            "data":
            [
                26962.000000,24464.000000,5705.000000,28145.000000,
                23281.000000,16827.000000,9961.000000,491.000000
            ]
        }
    }
}

4. Compile

To compile the source code, you need cmake tool to create its makefile or visualstudio solution and then compile the project:

cmake -H. -Bbuild

After its compilation, apart from generating the zetjsoncpp library, it will generate executables called test and test_file respectively. The executable called test does basic operations to check its integrity, meanwhile test_file reads a JSON file with content seen in section 3..

5. Conclusion

This article has presented a straightforward way to deserialize a json content from string or file in a single line of code. The results are very optimal for production and because it is binded in a C structure, the user can know each field easily so it's clear to deal with. I hope you find this tool useful.

History

2021-04-7 ZetJsonCpp 2.0.1

  • Fix bug when jsonvar c++ value mismatches with readed value

2021-02-15 ZetJsonCpp 2.0.0

  • Added support for map/dictionary of elements
  • Massive code organization and clean up
  • Support uft-8 files with bom
  • Support deserialize any json type (i.e., number/string/boolean/object, vector or map of elements)

2018-05-15 ZetJsonCpp 1.3.0

  • Review version history to new format (MAJOR.MINOR.PATCH)
  • Behaviour on error/warning is done through a callback setFunctionOnError, setFunctionOnWarning. If these functions are not set, then the message is printed with fprintf (stderr)
  • Added memory leak detection (only for GNU toolchain). It needs memmanager https://github.org/jespa007/memmanager and pass -DMEMMANAGER parameter on cmake
  • Fixed compile test_json shared library on MSVC platform
  • Improve cmake message prints

2018-05-10 ZetJsonCpp 1.2.0

  • Project is built through cmake
  • MSVC Support (v141 Tools or MSVC++ 2015)
  • Added zetjsoncpp namespace
  • Renamed fastjsoncpp to zetjsoncpp
  • Added feature detect array types
  • Changed GPL3 license to MIT

2015-08-29 ZetJsonCpp 1.1.0

  • Added feature support number as scientific notation (i.e., 2.1E-10)
  • Added feature on detecting property group or property group arrays
  • Fixed carry return line feeds compatible for Windows

2014-08-08 ZetJsonCpp 1.0.0

  • First release

License

This article, along with any associated source code and files, is licensed under The MIT License