Memetakan data dalam format json ke struktur c ++ dan kembali (mengerjakan kesalahan)

Versi sebelumnya memecahkan masalah pemetaan antara struktur C ++ dan json ternyata seperti pancake pertama - benjolan. Untungnya, pengembangan adalah proses berulang dan akan selalu ada versi kedua di belakang versi pertama. Komentar (terima kasih semuanya) dan analisis lubang di pancake pertama membuat beberapa perbaikan.



Apa yang buruk?



  • tidak mungkin untuk menggunakan struktur C ++ biasa (termasuk yang sudah ada). Semua struktur harus ditentukan dari awal dengan cara khusus
  • Objek Json hanya dapat dipetakan ke struktur yang ditentukan secara khusus
  • Json array hanya dapat dipetakan ke kelas khusus
  • tidak mungkin menggunakan wadah stl
  • makro hanya perlu (dimungkinkan tanpa mereka, tetapi pendaftaran metode untuk pengaturan bidang secara kaku dikombinasikan dengan inisialisasi bidang ini, oleh karena itu, tanpa makro, definisi struktur tidak dapat dibaca)
  • tampilan tidak dapat dikonfigurasi dengan cara apa pun, mis. tidak dapat diatur, misalnya, nilai default atau batas nilai


Bagaimana jadinya sekarang



Pendaftaran bidang yang akan berpartisipasi dalam tampilan tidak lagi terikat pada struktur. Untuk mendaftar, gunakan fungsinya



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


  • ptr - penunjuk ke bidang
  • name - nama bidang
  • options - pilihan tampilan


Berikut ini dapat digunakan sebagai jenis bidang:



  • bool
  • char, unsigned char, short, unsigned short, int unsigned int, long, long long
  • mengapung, ganda
  • std :: string
  • std :: daftar
  • std :: vektor
  • 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
        ]
      }
    ]
  }
}




  • ++ ( )
  • Objek Json dapat dipetakan baik ke struktur yang didefinisikan secara khusus (kemungkinan ini tetap) dan ke struktur biasa
  • Array Json dapat dipetakan ke std :: vector dan std :: list. Persyaratan umum untuk wadah tempat array dapat dipetakan belum sepenuhnya terbentuk.
  • objek json dapat dipetakan ke wadah asosiatif, dengan batasan bahwa kunci harus berupa string. Persyaratan umum untuk wadah, serta dengan susunan, belum sepenuhnya terbentuk.
  • makro tidak perlu dan tentu saja tidak perlu. Kemungkinan untuk menggunakannya tetap (sebagai opsi) selama pendaftaran, dikombinasikan dengan inisialisasi bidang. Tapi kemungkinan besar itu akan dipotong.
  • tampilan dapat disesuaikan menggunakan opsi


Perpustakaan tersedia di GitHub




All Articles