Zuordnen von Daten im JSON-Format zu einer C ++ - Struktur und zurück (Arbeiten an Fehlern)

Die vorherige Version zur Lösung des Problems der Zuordnung zwischen der C ++ - Struktur und json stellte sich wie der erste Pfannkuchen heraus - ein Klumpen. Glücklicherweise ist die Entwicklung ein iterativer Prozess und es wird immer eine zweite Version hinter der ersten Version geben. Die Kommentare (danke an alle) und die Lochanalyse im ersten Pfannkuchen haben einige Verbesserungen gebracht.



Was war schlecht



  • Es ist unmöglich, gewöhnliche C ++ - Strukturen (einschließlich der bereits vorhandenen) zu verwenden. Alle Strukturen müssen in besonderer Weise von Grund auf neu definiert werden
  • Das Json-Objekt kann nur einer speziell definierten Struktur zugeordnet werden
  • Das Json-Array kann nur einer speziellen Klasse zugeordnet werden
  • Es ist unmöglich, stl-Behälter zu verwenden
  • Makros sind einfach notwendig (es ist ohne sie möglich, aber die Registrierung von Methoden zum Setzen von Feldern ist starr mit der Initialisierung dieser Felder verbunden, daher ist die Strukturdefinition ohne Makros nicht lesbar).
  • die Anzeige ist in keiner Weise konfigurierbar, d.h. Es können beispielsweise keine Standardwerte oder Wertgrenzen festgelegt werden


Wie es jetzt wurde



Die Registrierung der Felder, die an der Anzeige teilnehmen, ist nicht mehr an die Struktur gebunden. Verwenden Sie die Funktion, um sich zu registrieren



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


  • ptr - Zeiger auf das Feld
  • name - Feldname
  • options - Anzeigeoptionen


Folgendes kann als Feldtyp verwendet werden:



  • Bool
  • char, unsigned char, short, unsigned short, int unsigned int, long, long long
  • schweben, doppelt
  • 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
        ]
      }
    ]
  }
}




  • ++ ( )
  • Json-Objekte können sowohl einer speziell definierten Struktur (diese Möglichkeit bleibt bestehen) als auch gewöhnlichen Strukturen zugeordnet werden
  • Json-Arrays können std :: vector und std :: list zugeordnet werden. Die allgemeinen Anforderungen für Container, auf die Arrays abgebildet werden können, sind noch nicht vollständig festgelegt.
  • JSON-Objekte können assoziativen Containern zugeordnet werden, mit der Einschränkung, dass der Schlüssel eine Zeichenfolge sein muss. Die allgemeinen Anforderungen an Container sowie an Arrays sind noch nicht vollständig erfüllt.
  • Makros sind unnötig und sicherlich nicht notwendig. Die Möglichkeit, sie zu verwenden, blieb (als Option) während der Registrierung in Verbindung mit der Initialisierung der Felder bestehen. Aber höchstwahrscheinlich wird es geschnitten.
  • Die Anzeige kann mithilfe von Optionen angepasst werden


Die Bibliothek ist auf GitHub verfügbar




All Articles