Asignación de datos en formato json a una estructura c ++ y viceversa (trabajo en errores)

La versión anterior de resolver el problema de mapeo entre la estructura de C ++ y json resultó ser el primer panqueque: un bulto. Afortunadamente, el desarrollo es un proceso iterativo y siempre habrá una segunda versión detrás de la primera versión. Los comentarios (gracias a todos) y el análisis de agujeros en el primer panqueque hicieron algunas mejoras.



Lo que era malo



  • Es imposible utilizar estructuras normales de C ++ (incluidas las ya existentes). Todas las estructuras deben definirse desde cero de una manera especial.
  • El objeto Json solo se puede asignar a una estructura especialmente definida
  • La matriz Json solo se puede asignar a una clase especial
  • es imposible usar contenedores stl
  • las macros son simplemente necesarias (es posible sin ellas, pero el registro de métodos para establecer campos se combina rígidamente con la inicialización de estos campos, por lo tanto, sin macros, la definición de la estructura es ilegible)
  • la pantalla no es configurable de ninguna manera, es decir no se pueden establecer, por ejemplo, valores predeterminados o límites de valores


Como se hizo ahora



El registro de los campos que participarán en la pantalla ya no está vinculado a la estructura. Para registrarse, use la función



reg(V T::* ptr, std::string const & name, Options<U>&& ... options);


  • ptr - puntero al campo
  • name - nombre del campo
  • options - opciones de pantalla


Los siguientes pueden usarse como tipos de campo:



  • bool
  • char, unsigned char, corto, unsigned short, int unsigned int, long, long long
  • flotador, doble
  • std :: string
  • std :: list
  • std :: vector
  • std::map ( std::string)
  • std::unordered_map ( std::string)
  • std::multimap ( std::string)
  • std::unordered_multimap ( std::string)
  • ++




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

struct MiB {
 std::list<Friend> friends;
 std::vector<std::list<std::string>> groups;
 std::map<std::string, std::vector<std::string>> books;
};

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

struct_mapping::reg(&MiB::friends, "friends");
struct_mapping::reg(&MiB::groups, "groups");
struct_mapping::reg(&MiB::books, "books");


, ,



map_json_to_struct(T & result_struct, std::basic_istream<char> & json_data);


  • result_struct
  • json_data — json


. .





#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;
}




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




, json . , :



MemberString::set(From function_from_string_, To function_to_string_);


  • function_from_string_
  • function_to_string_




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";
  }
 });




#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;
}




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




,



  • Bounds
  • Default
  • NotEmpty
  • Required


Bounds



, ( ) . . — . .



Bounds{ ,  }


:



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


Default



. bool, , , , , ++ . — .



Default{  }


:



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


NotEmpty



, . . . , .



:



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


Required



, . bool, , , , , ++ . . , .



:



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




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

#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;

 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;

  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"}});

 Spacecraft starship;

 std::istringstream json_data(R"json(
 {
  "in_development": false,
  "name": "Vostok",
  "stages": {
   "first": {
    "engine_count": 31,
    "fuel": "compressed gas",
    "length": 70
   },
   "second": {}
  }
 }
 )json");

 sm::map_json_to_struct(starship, json_data);

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




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


c++ json



json , ,



reg(V T::* ptr, std::string const & name, Options<U>&& ... options);




map_struct_to_json(T & source_struct, std::basic_ostream<char> & json_data, std::string indent);


  • source_struct
  • json_data — json
  • indent — ( , )


#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;
}




{
  "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
        ]
      }
    ]
  }
}




  • ++ ( )
  • Los objetos Json pueden asignarse tanto a una estructura especialmente definida (esta posibilidad permanece) como a estructuras ordinarias
  • Las matrices Json se pueden asignar a std :: vector y std :: list. Los requisitos generales para los contenedores en los que se pueden asignar matrices aún no están completamente formados.
  • Los objetos json se pueden asignar a contenedores asociativos, con la restricción de que la clave debe ser una cadena. Los requisitos generales para contenedores, así como con matrices, aún no están completamente formados.
  • Las macros son innecesarias y ciertamente no necesarias. La posibilidad de usarlos permaneció (como una opción) durante el registro, combinado con la inicialización de los campos. Pero lo más probable es que se corte.
  • la pantalla se puede personalizar usando las opciones


La biblioteca está disponible en GitHub.




All Articles