Mapping json to and from a c++ structure

Overview

StructMapping

Release version

документация на русском

English translation provided by translate.google.com

Table of contents

Mapping json to and from a c++ structure

I would like to define a c++ structure

struct Person {
 std::string name;
 int age;
 bool student;
} person;

pass the person instance to the mapping method along with json data

map_json_to_struct(person, json_data)

then use the filled structure

std::cout << person.name << " : " << person.age;

Or vice versa

map_struct_to_json(person, json_data, "  ");
std::cout << json_data.str() << std::endl;

get Person as json

{
 "name": "Agent K",
 "age": 42,
 "student": false
}

StructMapping is trying to solve these problems

Compatibility

Compilation is required with -std=c++17 Mainly for:

  • if constexpr
  • static inline

Compiler | platform combinations on which StructMapping has been tested:

  • GNU C++ 10.1.0 | Linux
  • Visual C++ 2019 and Microsoft (R) C/C++ Optimizing Compiler Version 19.26.28806 for x64 | Windows 64-bit (except tests)

As types of member-data can be used

  • bool
  • char, unsigned char, short, unsigned short, int unsigned int, long, long long
  • float, double
  • std::string
  • std::list
  • std::vector
  • std::set (expected to be represented in json as an array)
  • std::unordered_set (expected to be represented in json as an array)
  • std::multiset (expected to be represented in json as an array)
  • std::unordered_multiset (expected to be represented in json as an array)
  • std::map (the key can only be std::string)
  • std::unordered_map (the key can only be std::string)
  • std::multimap (the key can only be std::string)
  • std::unordered_multimap (the key can only be std::string)
  • c++ structure
  • enumerations

Installation

StructMapping is a header-only C++ library. All library files are in the include folder.

To build examples and run tests proceed with the steps below (cmake required):

  1. (if necessary) set the environment variables CC и CXX:
  • export CC=/usr/bin/gcc
  • export CXX=/usr/bin/g++
  1. create directory called build in StructMapping source directory
  2. change to build directory
  3. run cmake .. command to configure your build
  4. run cmake --build . command. On successful build you will find binary files for examples and tests (under windows tests are not build) in the bin directory
  5. (not for windows) to run the tests run ctest (you can get detailed output using ctest -V)
  6. it is possible to install library system-wide by running cmake --install . command from the build tree with administrative privileges. This will install all files according to system preferences.

Usage

Implementing a scenario with a Person structure

example/person

define the structure

struct Person {
 std::string name;
 int age;
 bool student;
};

register members

struct_mapping::reg(&Person::name, "name");
struct_mapping::reg(&Person::age, "age");
struct_mapping::reg(&Person::student, "student");

create an instance

Person person;

set json data

std::istringstream json_data(R"json(
{
 "name": "Jeebs",
 "age": 42,
 "student": true
}
)json");

pass the person instance to the mapping method along with json data

map_json_to_struct(person, json_data);

use

std::cout <<
 person.name << " : " <<
 person.age << " : " <<
 std::boolalpha << person.student << std::endl;

Completed code

#include <iostream>
#include <sstream>

#include "struct_mapping/struct_mapping.h"

struct Person {
 std::string name;
 int age;
 bool student;
};

int main() {
 struct_mapping::reg(&Person::name, "name");
 struct_mapping::reg(&Person::age, "age");
 struct_mapping::reg(&Person::student, "student");

 Person person;

 std::istringstream json_data(R"json(
  {
   "name": "Jeebs",
   "age": 42,
   "student": true
  }
 )json");

 struct_mapping::map_json_to_struct(person, json_data);

 std::cout <<
  person.name << " : " <<
  person.age << " : " <<
  std::boolalpha << person.student << std::endl;
}

result

Jeebs : 42 : true

Mapping json to c++ structure

To map json to a structure, it is necessary to register all data members of all structures that you want to mapped using for each field

template<typename T, typename V, typename ... U, template<typename> typename ... Options>
inline void reg(V T::* ptr, std::string const & name, Options<U>&& ... options);
  • ptr - pointer to data member
  • name - member name
  • options - mapping options

and call the function

template<typename T>
void map_json_to_struct(T & result_struct, std::basic_istream<char> & json_data);
  • result_struct - reference to the resulting structure
  • json_data - reference to json data input stream

During the mapping process, the correspondence between the types of data members and the types of set value is checked, and (for numbers) the set value is checked to get out of the range of values of the data member type. In case of type mismatch or out of range values exceptions are generated.

Enumeration

Enums in json will be represented as strings. Therefore, to use enumerations, it is required to establish conversion methods from string to enumeration value and vice versa, using:

template<typename From, typename To>
static void MemberString::set(From function_from_string_, To function_to_string_);
  • function_from_string_ - conversion function from string to enum value
  • function_to_string_ - conversion function from enum value to string

for example

enum class Color {
 red,
 blue,
 green,
};

struct_mapping::MemberString<Color>::set(
 [] (const std::string & value) {
  if (value == "red") return Color::red;
  if (value == "green") return Color::green;
  if (value == "blue") return Color::blue;

  throw struct_mapping::StructMappingException("bad convert '" + value + "' to Color");
 },
 [] (Color value) {
  switch (value) {
  case Color::red: return "red";
  case Color::green: return "green";
  default: return "blue";
  }
 });

or under enumeration example

Structures represented in json as strings

Structures in json can be represented as strings. To use this method, you need to set conversion methods from string to structure and vice versa using:

template<typename From, typename To>
static void MemberString::set(From function_from_string_, To function_to_string_);
  • function_from_string_ - conversion function from string to struct
  • function_to_string_ - conversion function from struct to string

for example

struct Color {
 int value;
};

struct_mapping::MemberString<Color>::set(
 [] (const std::string & value) {
  if (value == "red") return Color{1};
  if (value == "blue") return Color{2};

  throw struct_mapping::StructMappingException("bad convert '" + value + "' to Color");
 },
 [] (Color color) {
  switch (color.value) {
  case 1: return "red";
  case 2: return "blue";
  default: return "green";
  }
 }
 });

or under an example of using structures represented in json as strings

simple types example

example/simple

#include <iostream>
#include <sstream>

#include "struct_mapping/struct_mapping.h"

struct Planet {
 bool giant;
 long long surface_area;
 double mass;
 std::string satellite;
};

int main() {
 struct_mapping::reg(&Planet::giant, "giant");
 struct_mapping::reg(&Planet::surface_area, "surface_area");
 struct_mapping::reg(&Planet::mass, "mass");
 struct_mapping::reg(&Planet::satellite, "satellite");

 Planet earth;

 std::istringstream json_data(R"json(
  {
   "giant": false,
   "surface_area": 510072000000000,
   "mass": 5.97237e24,
   "satellite": "Moon"
  }
 )json");

 struct_mapping::map_json_to_struct(earth, json_data);

 std::cout << "earth" << std::endl;
 std::cout << " giant        : " << std::boolalpha << earth.giant << std::endl;
 std::cout << " surface_area : " << earth.surface_area << std::endl;
 std::cout << " mass         : " << earth.mass << std::endl;
 std::cout << " satellite    : " << earth.satellite << std::endl;
}

result

earth
 giant        : false
 surface_area : 510072000000000
 mass         : 5.97237e+24
 satellite    : Moon

structure example

example/struct

#include <iostream>
#include <sstream>

#include "struct_mapping/struct_mapping.h"

struct President {
 std::string name;
 double mass;
};

struct Earth {
 President president;
};

int main() {
 struct_mapping::reg(&President::name, "name");
 struct_mapping::reg(&President::mass, "mass");

 struct_mapping::reg(&Earth::president, "president");

 Earth earth;

 std::istringstream json_data(R"json(
  {
   "president": {
    "name": "Agent K",
    "mass": 75.6
   }
  }
 )json");

 struct_mapping::map_json_to_struct(earth, json_data);

 std::cout << "earth.president:" << std::endl;
 std::cout << " name : " << earth.president.name << std::endl;
 std::cout << " mass : " << earth.president.mass << std::endl;
}

result

earth.president:
 name : Agent K
 mass : 75.6

an example of using structures represented in json as strings

example/struct_from_string

#include <iostream>
#include <list>
#include <map>
#include <set>
#include <sstream>
#include <string>

#include "struct_mapping/struct_mapping.h"

namespace sm = struct_mapping;

struct Color {
 int value;

 bool operator<(const Color & o) const {
  return value < o.value;
 }
};

Color color_from_string(const std::string & value) {
 if (value == "red") return Color{1};
 if (value == "blue") return Color{2};

 return Color{0};
}

std::string color_to_string(const Color & color) {
 switch (color.value) {
 case 1: return "red";
 case 2: return "blue";
 default: return "green";
 }
}

struct Background {
 Color color;
};

struct Palette {
 Color main_color;
 Background background;
 std::list<Color> special_colors;
 std::set<Color> old_colors;
 std::map<std::string, Color> new_colors;

 friend std::ostream & operator<<(std::ostream & os, const Palette & o) {
  os << "main_color         : " << color_to_string(o.main_color) << std::endl;
  os << "background.color   : " << color_to_string(o.background.color) << std::endl;
  os << "special_colors     : ";
  for (auto color : o.special_colors) os << color_to_string(color) << ", ";
  os << std::endl << "old_colors         : ";
  for (auto color : o.old_colors) os << color_to_string(color) << ", ";
  os << std::endl << "new_colors         : ";
  for (auto [name, color] : o.new_colors) os << "[" << name << ", " << color_to_string(color) << "], ";
  os << std::endl;

  return os;
 }
};

int main() {
 sm::MemberString<Color>::set(color_from_string, color_to_string);

 sm::reg(&Palette::main_color, "main_color", sm::Default{"red"});
 sm::reg(&Palette::background, "background", sm::Required{});
 sm::reg(&Palette::special_colors, "special_colors");
 sm::reg(&Palette::old_colors, "old_colors");
 sm::reg(&Palette::new_colors, "new_colors");

 sm::reg(&Background::color, "color");

 Palette palette;

 std::istringstream json_data(R"json(
 {
  "background": {
   "color": "blue"
  },
  "special_colors": ["red", "green", "red", "blue"],
  "old_colors": ["red", "green", "blue"],
  "new_colors": {
   "dark": "green",
   "light": "red",
   "neutral": "blue"
  }
 }
 )json");

 sm::map_json_to_struct(palette, json_data);

 std::cout << palette << std::endl;
}

result

main_color         : red
background.color   : blue
special_colors     : red, green, red, blue, 
old_colors         : green, red, blue, 
new_colors         : [dark, green], [light, red], [neutral, blue],

Enumeration example

example/enumeration

#include <iostream>
#include <list>
#include <map>
#include <sstream>
#include <string>

#include "struct_mapping/struct_mapping.h"

namespace sm = struct_mapping;

enum class Color {
 red,
 blue,
 green,
};

Color color_from_string(const std::string & value) {
 if (value == "red") return Color::red;
 if (value == "blue") return Color::blue;

 return Color::green;
}

std::string color_to_string(Color color) {
 switch (color) {
 case Color::red: return "red";
 case Color::green: return "green";
 default: return "blue";
 }
}

struct Palette {
 Color main_color;
 Color background_color;
 std::list<Color> special_colors;
 std::map<std::string, Color> colors;

 friend std::ostream & operator<<(std::ostream & os, const Palette & o) {
  os << "main_color       : " << color_to_string(o.main_color) << std::endl;
  os << "background_color : " << color_to_string(o.background_color) << std::endl;
  os << "special_colors   : ";
  for (auto color : o.special_colors) os << color_to_string(color) << ", ";
  os << std::endl << "colors           : ";
  for (auto [name, color] : o.colors) os << "[" << name << ", " << color_to_string(color) << "], ";
  os << std::endl;

  return os;
 }
};

int main() {
 sm::MemberString<Color>::set(color_from_string, color_to_string);

 sm::reg(&Palette::main_color, "main_color", sm::Required{});
 sm::reg(&Palette::background_color, "background_color", sm::Default{Color::blue});
 sm::reg(&Palette::special_colors, "special_colors");
 sm::reg(&Palette::colors, "colors");

 Palette palette;

 std::istringstream json_data(R"json(
 {
  "main_color": "green",
  "special_colors": ["green", "green", "red"],
  "colors": {
   "dark": "green",
   "light": "red",
   "neutral": "blue"
  }
 }
 )json");

 sm::map_json_to_struct(palette, json_data);

 std::cout << palette << std::endl;
}

result

main_color       : green
background_color : blue
special_colors   : green, green, red, 
colors           : [dark, green], [light, red], [neutral, blue],

sequence container example

example/array_like

#include <iostream>
#include <list>
#include <set>
#include <sstream>
#include <string>
#include <unordered_set>
#include <vector>

#include "struct_mapping/struct_mapping.h"

struct Friend {
 std::string name;
 std::set<int> counters;

 bool operator==(const Friend & o) const {
  return name == o.name;
 }
};

struct Friend_hash {
 size_t operator()(const Friend & o) const {
  return static_cast<size_t>(o.name.size());
 }
};

struct MiB {
 std::unordered_set<Friend, Friend_hash> friends;
 std::vector<std::list<std::string>> alien_groups;
 std::vector<std::list<std::vector<std::string>>> planet_groups;
};

int main() {
 struct_mapping::reg(&Friend::name, "name");
 struct_mapping::reg(&Friend::counters, "counters");

 struct_mapping::reg(&MiB::friends, "friends");
 struct_mapping::reg(&MiB::alien_groups, "alien_groups");
 struct_mapping::reg(&MiB::planet_groups, "planet_groups");

 std::istringstream json_data(R"json(
  {
   "friends": [
    {
     "name": "Griffin",
     "counters": [1,3,4]
    },
    {
     "name": "Boris",
     "counters": []
    },
    {
     "name": "Agent K",
     "counters": [42, 128]
    }
   ],
   "alien_groups": [
    [
     "Edgar the Bug",
     "Boris the Animal",
     "Charlie",
     "Serleena"
    ],
    [
     "Agent J",
     "Agent K",
     "Zed",
     "Griffin",
     "Roman the Fabulist"
    ]
   ],
   "planet_groups": [
    [
     [
      "Mercury",
      "Venus",
      "Earth",
      "Mars"
     ],
     [
      "Jupiter",
      "Saturn",
      "Uranus",
      "Neptune"
     ]
    ],
    [
     [
      "Titan",
      "Ganymede"
     ],
     [
      "Eris",
      "Titania"
     ]
    ]
   ]
  }
 )json");

 MiB mib;

 struct_mapping::map_json_to_struct(mib, json_data);

 std::cout << "mib:" << std::endl;

 std::cout << " friends :" << std::endl;
 for (auto& f : mib.friends) {
  std::cout << "  name: [ " << f.name << " ], counters: [";
  for (auto& c : f.counters) {
   std::cout << c << ", ";
  }
  std::cout << "]" << std::endl;
 }

 std::cout << std::endl << " aliens_groups :" << std::endl;
 for (auto& alien : mib.alien_groups) {
  for (auto& name : alien) {
   std::cout << "  " << name << std::endl;
  }
  std::cout << std::endl;
 }

 std::cout << " planets_groups :" << std::endl;
 for (auto& group : mib.planet_groups) {
  std::cout << "  ---" << std::endl;
  for (auto& category : group) {
   for (auto& planet : category) {
    std::cout << "   " << planet << std::endl;
   }
   std::cout << std::endl;
  }
  std::cout << std::endl;
 }

 std::cout << std::endl;
}

result

mib:
 friends :
  name: [ Griffin ], counters: [1, 3, 4, ]
  name: [ Boris ], counters: []
  name: [ Agent K ], counters: [42, 128, ]

  Agent J
  Agent K
  Zed
  Griffin
  Roman the Fabulist

 planets_groups :
  ---
   Mercury
   Venus
   Earth
   Mars

   Jupiter
   Saturn
   Uranus
   Neptune


  ---
   Titan
   Ganymede

   Eris
   Titania

associative container example

example/map_like

#include <iostream>
#include <map>
#include <sstream>
#include <unordered_map>

#include "struct_mapping/struct_mapping.h"

struct Library {
 std::unordered_map<std::string, std::multimap<std::string, int>> counters;
 std::multimap<std::string, std::unordered_multimap<std::string, std::string>> books;
};

int main() {
 struct_mapping::reg(&Library::counters, "counters");
 struct_mapping::reg(&Library::books, "books");

 Library library;

 std::istringstream json_data(R"json(
  {
   "counters": {
    "first": {
     "112": 13,
     "142": 560,
     "112": 0
    },
    "second": {
     "2": 28,
     "20": 411
    },
    "third": {
    }
   },
   "books": {
    "asd": {
     "Leo": "aaa",
     "Leo": "bbb",
     "Mark": "ccc"
    },
    "wwert": {
     "Gogol": "ddd",
     "Tom": "eee"
    }
   }
  }
 )json");

 struct_mapping::map_json_to_struct(library, json_data);

 std::cout << "library:" << std::endl;

 std::cout << " counters :" << std::endl;
 for (auto [n1, v] : library.counters) {
  std::cout << "  " << n1 << " : ";
  for (auto [n2, c] : v) {
   std::cout << "[" << n2 << ", " << c << "], ";
  }
  std::cout << std::endl;
 }

 std::cout << " books :" << std::endl;
 for (auto [n1, v] : library.books) {
  std::cout << "  " << n1 << " : ";
  for (auto [n2, b] : v) {
   std::cout << "[" << n2 << ", " << b << "], ";
  }
  std::cout << std::endl;
 }

 std::cout << std::endl;
}

result

library:
 counters :
  third : 
  second : [2, 28], [20, 411], 
  first : [112, 13], [112, 0], [142, 560], 
 books :
  asd : [Mark, ccc], [Leo, bbb], [Leo, aaa], 
  wwert : [Tom, eee], [Gogol, ddd],

Mapping options

When registering a data member, you can specify one or more options that will customize the mapping.

Bounds

Sets the range of values in which (including the limits of the range) the value to be set is located. Applicable for integer types and floating point types. The option accepts two parameters - the range limits. Throws an exception when the set during the mapping of the value goes out of bounds.

Bounds{lower, upper}

Example of setting an option:

reg(&Stage::engine_count, "engine_count", Bounds{1, 31});
Default

Sets the default value for the data member. Applicable for bool, integer types, floating point types, strings, containers, cpp structures and enumerations. The option accepts one parameter - the default value.

Default{default_value}

Example of setting an option:

reg(&Stage::engine_count, "engine_count", Default{3});

When using an option for a struct for which conversion to / from string is used (structures represented in json as strings), the default value must also be a string

sm::reg(&Palette::main_color, "main_color", sm::Default{"red"});
NotEmpty

Notes that a data member cannot be set to an empty value. Applicable for strings and containers. The option does not accept parameters. Throws an exception if, after completion of the mapping, the field value is an empty string or an empty container.

Example of setting an option:

reg(&Spacecraft::name, "name", NotEmpty{}));
Required

Notes that a data member must be set to a value. Applicable for bool, integer types, floating point types, strings, containers, cpp structures and enumerations. The option does not accept parameters. Throws an exception if, after completion of the mapping, value for the field has not been set.

Example of setting an option:

reg(&Spacecraft::name, "name", Required{}));
Options example

example/options

#include <iostream>
#include <list>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <unordered_set>

#include "struct_mapping/struct_mapping.h"

namespace sm = struct_mapping;

struct Stage {
 unsigned short engine_count;
 std::string fuel;
 long length;

 friend std::ostream & operator<<(std::ostream & os, const Stage & o) {
  os << "  engine_count : " << o.engine_count << std::endl;
  os << "  fuel         : " << o.fuel << std::endl;
  os << "  length       : " << o.length << std::endl;

  return os;
 }
};

struct Spacecraft {
 bool in_development;
 std::string name;
 int mass;
 std::map<std::string, Stage> stages;
 std::list<std::string> crew;
 std::set<int> ids;
 std::unordered_set<std::string> tools;

 friend std::ostream & operator<<(std::ostream & os, const Spacecraft & o) {
  os << "in_development : " << std::boolalpha << o.in_development << std::endl;
  os << "name           : " << o.name << std::endl;
  os << "mass           : " << o.mass << std::endl;
  os << "stages: " << std::endl;
  for (auto& s : o.stages) os << " " << s.first << std::endl << s.second;
  os << "crew: " << std::endl;
  for (auto& p : o.crew) os << " " << p << std::endl;
  os << "ids: " << std::endl;
  for (auto& i : o.ids) os << " " << i << std::endl;
  os << "tools: " << std::endl;
  for (auto& t : o.tools) os << " " << t << std::endl;

  return os;
 }
};

int main() {
 sm::reg(&Stage::engine_count, "engine_count", sm::Default{6}, sm::Bounds{1, 31});
 sm::reg(&Stage::fuel, "fuel", sm::Default{"subcooled"});
 sm::reg(&Stage::length, "length", sm::Default{50});

 sm::reg(&Spacecraft::in_development, "in_development", sm::Required{});
 sm::reg(&Spacecraft::name, "name", sm::NotEmpty{});
 sm::reg(&Spacecraft::mass, "mass", sm::Default{5000000}, sm::Bounds{100000, 10000000});
 sm::reg(&Spacecraft::stages, "stages", sm::NotEmpty{});
 sm::reg(&Spacecraft::crew, "crew", sm::Default{std::list<std::string>{"Arthur", "Ford", "Marvin"}});
 sm::reg(&Spacecraft::ids, "ids", sm::Default{std::set<int>{14, 159}});
 sm::reg(&Spacecraft::tools, "tools", sm::NotEmpty{});

 Spacecraft starship;

 std::istringstream json_data(R"json(
 {
  "in_development": false,
  "name": "Vostok",
  "stages": {
   "first": {
    "engine_count": 31,
    "fuel": "compressed gas",
    "length": 70
   },
   "second": {}
  },
  "tools": [
   "Reverberating Carbonizer With Mutate Capacity",
   "Noisy Cricket",
   "The Series 4 De-Atomizer"
  ]
 }
 )json");

 sm::map_json_to_struct(starship, json_data);

 std::cout << starship << std::endl;
}

result

in_development : false
name           : Vostok
mass           : 5000000
stages: 
 first
  engine_count : 31
  fuel         : compressed gas
  length       : 70
 second
  engine_count : 6
  fuel         : subcooled
  length       : 50
crew: 
 Arthur
 Ford
 Marvin
ids: 
 14
 159
tools: 
 The Series 4 De-Atomizer
 Noisy Cricket
 Reverberating Carbonizer With Mutate Capacit

Reverse mapping of c++ structure to json

For the structure to be mapped back to json, it is necessary to register all data members of all the structures that need to be mapped using for each field

template<typename T, typename V, typename ... U, template<typename> typename ... Options>
inline void reg(V T::* ptr, std::string const & name, Options<U>&& ... options);
  • ptr - pointer to data member
  • name - member name
  • options - mapping options

and call the function

template<typename T>
void map_struct_to_json(T & source_struct, std::basic_ostream<char> & json_data, std::string indent);
  • source_struct - reference to the source structure
  • json_data - reference to json data output stream
  • indent - indentation (if set, makes the output format better readable)

example/struct_to_json

#include <iostream>
#include <sstream>

#include "struct_mapping/struct_mapping.h"

struct OceanPart {
 std::string name;
 double average_depth;
 std::vector<int> temperature;
};

struct OceanColor {
 std::string name;
};

struct Ocean {
 double water_volume;
 long long surface_area;
 bool liquid;
 std::string name;

 OceanColor color;

 std::vector<OceanPart> parts;
};

struct Planet {
 bool giant;
 long long surface_area;
 double mass;
 double volume;
 long long orbital_period;
 std::string name;
 bool terrestrial;
 std::string shape;

 Ocean ocean;
};

int main() {
 struct_mapping::reg(&OceanPart::name, "name");
 struct_mapping::reg(&OceanPart::average_depth, "average_depth");
 struct_mapping::reg(&OceanPart::temperature, "temperature");

 struct_mapping::reg(&OceanColor::name, "name");

 struct_mapping::reg(&Ocean::water_volume, "water_volume");
 struct_mapping::reg(&Ocean::surface_area, "surface_area");
 struct_mapping::reg(&Ocean::liquid, "liquid");
 struct_mapping::reg(&Ocean::name, "name");
 struct_mapping::reg(&Ocean::color, "color");
 struct_mapping::reg(&Ocean::parts, "parts");

 struct_mapping::reg(&Planet::giant, "giant");
 struct_mapping::reg(&Planet::surface_area, "surface_area");
 struct_mapping::reg(&Planet::mass, "mass");
 struct_mapping::reg(&Planet::volume, "volume");
 struct_mapping::reg(&Planet::orbital_period, "orbital_period");
 struct_mapping::reg(&Planet::name, "name");
 struct_mapping::reg(&Planet::terrestrial, "terrestrial");
 struct_mapping::reg(&Planet::shape, "shape");
 struct_mapping::reg(&Planet::ocean, "ocean");

 Planet earth;

 earth.giant = false;
 earth.terrestrial = true;
 earth.surface_area = 510072000;
 earth.orbital_period = 365 * 24 * 3600;
 earth.mass = 5.97237e24;
 earth.name = "Terra";
 earth.volume = 1.08321e12;
 earth.shape = "nearly spherical";

 earth.ocean.water_volume = 1332000000;
 earth.ocean.surface_area = 361132000;
 earth.ocean.liquid = true;
 earth.ocean.name = "World Ocean";
 earth.ocean.color.name = "blue";

 OceanPart pacific;
 pacific.name = "Pacific Ocean";
 pacific.average_depth = 4.280111;
 pacific.temperature = std::vector<int>{-3, 5, 12};

 OceanPart atlantic;
 atlantic.name = "Atlantic Ocean";
 atlantic.average_depth = 3.646;
 atlantic.temperature = std::vector<int>{-3, 0};

 earth.ocean.parts.push_back(pacific);
 earth.ocean.parts.push_back(atlantic);

 std::ostringstream json_data;
 struct_mapping::map_struct_to_json(earth, json_data, "  ");

 std::cout << json_data.str() << std::endl;
}

result

{
  "giant": false,
  "surface_area": 510072000,
  "mass": 5.97237e+24,
  "volume": 1.08321e+12,
  "orbital_period": 31536000,
  "name": "Terra",
  "terrestrial": true,
  "shape": "nearly spherical",
  "ocean": {
    "water_volume": 1.332e+09,
    "surface_area": 361132000,
    "liquid": true,
    "name": "World Ocean",
    "color": {
      "name": "blue"
    },
    "parts": [
      {
        "name": "Pacific Ocean",
        "average_depth": 4.28011,
        "temperature": [
          -3,
          5,
          12
        ]
      },
      {
        "name": "Atlantic Ocean",
        "average_depth": 3.646,
        "temperature": [
          -3,
          0
        ]
      }
    ]
  }
}

Registration of data members combined with initialization

In order not to take out the registration of data members from the structure, registration can be combined with initialization

example/in_struct_reg

#include <iostream>
#include <sstream>
#include <vector>

#include "struct_mapping/struct_mapping.h"

struct Planet {
 bool giant = [] {struct_mapping::reg(&Planet::giant, "giant"); return false;} ();
 long long surface_area = [] {struct_mapping::reg(&Planet::surface_area, "surface_area"); return 0;} ();
 double mass = [] {struct_mapping::reg(&Planet::mass, "mass"); return 0;} ();
 std::vector<std::string> satellites = [] {
  struct_mapping::reg(&Planet::satellites, "satellites"); return std::vector<std::string>{};} ();
};

int main() {
 std::istringstream json_data(R"json(
  {
   "giant": false,
   "surface_area": 510072000000000,
   "mass": 5.97237e24,
   "satellites": ["Moon", "R24"]
  }
 )json");

 Planet earth;

 struct_mapping::map_json_to_struct(earth, json_data);

 std::cout << "earth" << std::endl;
 std::cout << " giant        : " << std::boolalpha << earth.giant << std::endl;
 std::cout << " surface_area : " << earth.surface_area << std::endl;
 std::cout << " mass         : " << earth.mass << std::endl;
 std::cout << " satellite    : [ ";
 for (auto & s : earth.satellites) std::cout << s << ", ";
 std::cout << "]" << std::endl;
}

To simplify the use of this method, four macros are defined

  • BEGIN_STRUCT
  • MEMBER
  • MEMBER_OPTIONS
  • END_STRUCT

BEGIN_STRUCT

Defines the begin of the structure and its name

BEGIN_STRUCT(name)

BEGIN_STRUCT(Planet)

MEMBER

Adds a data member, registers it, and initializes it with a default value:

MEMBER(type, name)

MEMBER(bool, giant)
MEMBER(long long, surface_area)
MEMBER(double, mass)
MEMBER(std::vector<std::string>, satellites)

MEMBER_OPTIONS

Adds a data member and registers it. You can set field options. At least one option must be setted:

MEMBER_OPTIONS(type, name, option_1, ...)

MEMBER_OPTIONS(
	int,
	count,
	struct_mapping::Bounds{23, 47},
	struct_mapping::Default{35})

END_STRUCT

Defines the end of the structure

END_STRUCT

END_STRUCT

example/macro_reg

#include <iostream>
#include <sstream>
#include <string>
#include <vector>

#include "struct_mapping/struct_mapping.h"

BEGIN_STRUCT(Planet)

MEMBER(bool, giant)
MEMBER(long long, surface_area)
MEMBER_OPTIONS(
 double,
 mass,
 struct_mapping::Bounds{1.0e23, 1.0e27},
 struct_mapping::Default{1.0e25})
MEMBER(std::vector<std::string>, satellites)

END_STRUCT

int main() {
 std::istringstream json_data(R"json(
 {
  "giant": false,
  "surface_area": 510072000000000,
  "satellites": ["Moon", "R24"]
 }
 )json");

 Planet earth;

 struct_mapping::map_json_to_struct(earth, json_data);

 std::cout << "earth" << std::endl;
 std::cout << " giant        : " << std::boolalpha << earth.giant << std::endl;
 std::cout << " surface_area : " << earth.surface_area << std::endl;
 std::cout << " mass         : " << earth.mass << std::endl;
 std::cout << " satellite    : [ ";
 for (auto & s : earth.satellites) std::cout << s << ", ";
 std::cout << "]" << std::endl;
}

Exceptions

StructMapping throws a StructMappingException exception during the mapping process

  • when setting a value for an unregistered field (in json, the name of the value in the object does not correspond to any of the registered fields in the c++ structure)
  • when setting a value whose type does not match the type of field (for enumerations when setting values other than a string)
  • (for numerical fields) when setting a value that is outside the range of the field type
  • when using an enumeration, if functions for converting to / from a string were not specified for it
  • when setting a value that exceeds the limits set by the Bounds option
  • with an empty string field, if the NotEmpty option was set for it
  • if the Required option was set, but the value was not set
  • when setting the Bounds option to a value that is outside the range of values for the field type
  • when setting the Bounds option to values when the lower bound value is greater than the upper bound value
  • when setting the Default option to a value that is outside the range of values for the field type
  • when reaching the end of the json stream, when parsing is not completed (the process of parsing an object, value, etc.)
  • when a character is received in the json stream that is not valid at this position (for example, an array termination character, if the beginning character of the array was not received earlier)
  • when conversion of json string representing number to number fails
Comments
  • Omnet ++

    Omnet ++

    Hello ! i tried using the library in a project on omnet++ , and it shows that it doesn't recognize some functions used in the library as well as some ")" missing . i am using c++ 17 and gcc, g++ version 10.2

    opened by OverwatchGirl 7
  • how to ignore unknow fields?

    how to ignore unknow fields?

    This is a good library!

    While I am having a problem,

    {"code":100000,"message":"success", "encrypt":false}
    

    The json may be change in the future, changed may be as follows

    {"code":100000,"message":"success", "encrypt":false, "test":0}
    

    the program will crash after receive new json data,

    how can I ignore test field?

    opened by ckelsel 2
  • Json file

    Json file

    Hello !

    I am trying to convert data that i read from a json file into a data structure, but i always get this error :

    terminate called after throwingan instance of 'struct_mapping::StructMappingException' what(): parser: unexpected character 'j' at line 1

    I added "json(" at the beginning of the read data, and ")json" at its end.

    opened by OverwatchGirl 2
  • it looks like define macro something wrong

    it looks like define macro something wrong

    following is OK, default value is assigned

    struct Friend {
    	std::string name;
    	std::set<int> counters;
    };
    namespace sm = struct_mapping;
    
    /*
    BEGIN_STRUCT(Friend)
    MEMBER_OPTIONS(std::string, name, sm::Default{"mike"})
    MEMBER_OPTIONS(std::set<int>, counters, sm::Default{std::set<int>{1, 2, 3}})
    END_STRUCT
    */
    BEGIN_STRUCT(MiB)
    MEMBER_OPTIONS(std::vector<Friend>, friends, sm::Default{std::vector<Friend>{Friend{"mike", std::set<int>{1,2,3}}}})
    END_STRUCT
    
    
    int main()
    {
    	struct_mapping::reg(&Friend::name, "name");
    	struct_mapping::reg(&Friend::counters, "counters");
    
    	std::istringstream json_data(R"json(
     {
     }
     )json");
    
    	MiB mib;
    
    	struct_mapping::map_json_to_struct(mib, json_data);
    
    	std::ostringstream out_json_data;
    	struct_mapping::map_struct_to_json(mib, out_json_data, "  ");
    
    	std::cout << out_json_data.str() << std::endl;
    }
    

    output is:

    {
      "friends": [
        {
          "name": "mike",
          "counters": [
            1,
            2,
            3
          ]
        }
      ]
    }
    

    but, if define Friend using macro, it's not OK

    /*
    struct Friend {
    	std::string name;
    	std::set<int> counters;
    };
    */
    namespace sm = struct_mapping;
    
    BEGIN_STRUCT(Friend)
    MEMBER_OPTIONS(std::string, name, sm::Default{"mike"})
    MEMBER_OPTIONS(std::set<int>, counters, sm::Default{std::set<int>{1, 2, 3}})
    END_STRUCT
    
    BEGIN_STRUCT(MiB)
    MEMBER_OPTIONS(std::vector<Friend>, friends, sm::Default{std::vector<Friend>{Friend{"mike", std::set<int>{1,2,3}}}})
    END_STRUCT
    
    
    int main()
    {
    	/*
    	struct_mapping::reg(&Friend::name, "name");
    	struct_mapping::reg(&Friend::counters, "counters");
    	*/
    	std::istringstream json_data(R"json(
     {
     }
     )json");
    
    	MiB mib;
    
    	struct_mapping::map_json_to_struct(mib, json_data);
    
    	std::ostringstream out_json_data;
    	struct_mapping::map_struct_to_json(mib, out_json_data, "  ");
    
    	std::cout << out_json_data.str() << std::endl;
    }
    

    the output is:

    {
      "friends": [
        {
        }
      ]
    }
    
    opened by yanlusu 0
  • implementing teensy4.1/Arduino

    implementing teensy4.1/Arduino

    Hi there, i tried to implement struct_mapping to arduino/teensy project, as right now c++17 is not fully featured (or at least just with some effort) i wanted figure out if the effort is worth to update the platform to c++17 compatibility, as the library is not designed for use with arduino in first instance.

    so my question: does anybode tried to get it up and running on an arduino compatible board (like Teensy 4.1 in my case)?

    @bk192077 do you have any glue if this might work and maybe howto?

    thank you for advice

    opened by bernhardkurz 0
  • Is map_struct_to_json thread safe?

    Is map_struct_to_json thread safe?

    The following code sometimes gets the first half of json, I suspect it is because of the result of calling in a multi-threaded environment, the following fieldinfo is a local variable

    struct_mapping::map_struct_to_json(fieldInfo, oss, " ");

    opened by byrnexu 1
  • https://github.com/nlohmann/json

    https://github.com/nlohmann/json

    Hello. I'm doing an integration of the struct_mappings library and the nlohmann-json library to be able to map std::any objects.

    Greetings and happy new year.

    opened by cmachaca 0
  • How to handle a kind of json like this?

    How to handle a kind of json like this?

    [ { "field1": "Donald Duck", "field2": "sample text", "field3": 0.5675269775390624, "h": 490, "attrfield": [ { "attr1": "first attribute", "attr2": "secod attribute" }, { "attr1": "first attribute", "attr2": "secod attribute" } ] }, { "field1": "Mickey Mouse", "field2": "sample text", "field3": 0.9995269775390625, "h": 300, "attrfield": [ { "attr1": "first attribute", "attr2": "secod attribute" }, { "attr1": "first attribute", "attr2": "secod attribute" } ] } ]

    when i try to map it into a struct I reach allways the following error: terminate called after throwing an instance of 'struct_mapping::StructMappingException' what(): parser: unexpected character '[' at line 1

    opened by rgambardella66 2
Releases(v0.6.0)
  • v0.6.0(Dec 16, 2021)

    Added

    • as types of member-data can be used: std::set, std::unordered_set, std::multiset, std::unordered_multiset
    • structures in json can be represented as strings
    • options in macro
    • optional fields (std::optional)
    • Clang support
    Source code(tar.gz)
    Source code(zip)
  • v0.5.0(Jul 9, 2020)

    Added

    • use of enumerations as member and as element of container
    • option Required

    Changed

    • option Default for cpp structures
    • option Default for containers
    • option Default for enumeration
    • option NotEmpty for containers

    Fixed

    • if Boundes were set for fields with the same types, then all fields used the same range.
    Source code(tar.gz)
    Source code(zip)
  • v0.4.0(Jul 2, 2020)

    Added

    • check type limits on set value
    • check type on set value
    • option Bounds for integer and floating point
    • option Default for bool, integer, floating point and string
    • option NotEmpty for string
    • Visual C++ 2019 and Microsoft (R) C/C++ Optimizing Compiler Version 19.26.28806 for x64 | Windows 64-bit (except tests)

    Changed

    • exception messages
    • internal mapping mechanics

    Fixed

    • before starting mapping the states of F classes is reset
    • std::string is no longer treated as array_like

    Removed

    • unsigned long
    Source code(tar.gz)
    Source code(zip)
  • v0.3.0(Jun 23, 2020)

    Added

    • mapping of json data to common c++ structure
    • reverse mapping of common c++ structure to json data
    • as types of member-data can be used:
      • std::list
      • std::vector
      • std::map
      • std::unordered_map
      • std::multimap
      • std::unordered_multimap

    Changed

    • registration of data members combined with initialization made optional
    Source code(tar.gz)
    Source code(zip)
  • v0.2.1(Jun 16, 2020)

  • v0.2.0(Jun 14, 2020)

  • v0.1.0(Jun 14, 2020)

Owner
null
Mapping json to and from a c++ structure

StructMapping документация на русском English translation provided by translate.google.com Table of contents Introduction Compatibility Installation U

null 78 Dec 14, 2022
A generator of JSON parser & serializer C++ code from structure header files

JSON-CPP-gen This is a program that parses C++ structures from a header file and automatically generates C++ code capable of serializing said structur

Viktor Chlumský 12 Oct 13, 2022
This is a JSON C++ library. It can write and read JSON files with ease and speed.

Json Box JSON (JavaScript Object Notation) is a lightweight data-interchange format. Json Box is a C++ library used to read and write JSON with ease a

Anhero inc. 110 Dec 4, 2022
json-cpp is a C++11 JSON serialization library.

JSON parser and generator for C++ Version 0.1 alpha json-cpp is a C++11 JSON serialization library. Example #include <json-cpp.hpp> struct Foo {

Anatoly Scheglov 7 Oct 30, 2022
A convenience C++ wrapper library for JSON-Glib providing friendly syntactic sugar for parsing JSON

This library is a wrapper for the json-glib library that aims to provide the user with a trivial alternative API to the API provided by the base json-

Rob J Meijer 17 Oct 19, 2022
json-build is a zero-allocation JSON serializer compatible with C89

json-build is a zero-allocation JSON serializer compatible with C89. It is inspired by jsmn, a minimalistic JSON tokenizer.

Lucas Müller 31 Nov 16, 2022
📟 JSON library for Arduino and embedded C++. Simple and efficient.

ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things). Features JSON deserialization Optionally decodes UTF-16 escape sequences t

Benoît Blanchon 6k Jan 3, 2023
JSON parser and generator for C/C++ with scanf/printf like interface. Targeting embedded systems.

JSON parser and emitter for C/C++ Features ISO C and ISO C++ compliant portable code Very small footprint No dependencies json_scanf() scans a string

Cesanta Software 637 Dec 30, 2022
C library for encoding, decoding and manipulating JSON data

Jansson README Jansson is a C library for encoding, decoding and manipulating JSON data. Its main features and design principles are: Simple and intui

Petri Lehtinen 2.7k Dec 31, 2022
🗄️ single header json parser for C and C++

??️ json.h A simple single header solution to parsing JSON in C and C++. JSON is parsed into a read-only, single allocation buffer. The current suppor

Neil Henning 544 Jan 7, 2023
a JSON parser and printer library in C. easy to integrate with any model.

libjson - simple and efficient json parser and printer in C Introduction libjson is a simple library without any dependancies to parse and pretty prin

Vincent Hanquez 260 Nov 21, 2022
A simple class for parsing JSON data into a QVariant hierarchy and vice versa.

The qt-json project is a simple collection of functions for parsing and serializing JSON data to and from QVariant hierarchies. NOTE: Qt5 introduced a

null 305 Dec 13, 2022
RapidJSON is a JSON parser and generator for C++.

A fast JSON parser/generator for C++ with both SAX/DOM style API

Tencent 12.6k Dec 30, 2022
jq is a lightweight and flexible command-line JSON processor.

jq is a lightweight and flexible command-line JSON processor.

Stephen Dolan 23.9k Dec 31, 2022
A curated list of awesome things built with the JSON processor and turing-complete functional language jq.

A curated list of awesome things built with the JSON processor and turing-complete functional language jq.

fiatjaf 578 Jan 3, 2023
single-header json parser for c99 and c++

ghh_json.h a single-header ISO-C99 (and C++ compatible) json loader. why? obviously this isn't the first json library written for C, so why would I wr

garrison hinson-hasty 14 Dec 1, 2022
An easy-to-use and competitively fast JSON parsing library for C++17, forked from Bitcoin Cash Node's own UniValue library.

UniValue JSON Library for C++17 (and above) An easy-to-use and competitively fast JSON parsing library for C++17, forked from Bitcoin Cash Node's own

Calin Culianu 24 Sep 21, 2022
Fast JSON serialization and parsing in C++

DAW JSON Link v2 Content Intro Default Mapping of Types API Documentation - Member mapping classes and methods Cookbook Get cooking and putting it all

Darrell Wright 352 Jan 4, 2023
json_struct is a single header only C++ library for parsing JSON directly to C++ structs and vice versa

Structurize your JSON json_struct is a single header only library that parses JSON to C++ structs/classes and serializing structs/classes to JSON. It

Jørgen Lind 275 Dec 28, 2022