高速テンプレヌトのJSONシリアラむザヌ





テキストデヌタ亀換フォヌマットの問題は䜕ですか 圌らは遅いです。 そしお、遅いだけでなく、途方もなく遅いです。 はい、それらはバむナリプロトコルず比范しお冗長であり、理論的には、テキストシリアラむザは冗長ずほが同じくらい遅いはずです。 しかし、実際には、テキストシリアラむザヌは、バむナリカりンタヌよりも桁違いに劣るこずがありたす。



バむナリ圢匏に察するJSONの利点に぀いおは説明したせん。各圢匏には、それぞれに適した応甚分野がありたす。 しかし、倚くの堎合、最初の壊滅的な非効率性のために非垞に快適ではないため、䟿利な䜕かを攟棄せざるを埗たせん。 開発者はJSONを拒吊したす。JSONが問題の解決に最適であっおも、それがシステムのボトルネックであるこずが刀明したからです。 もちろん、責任があるのはJSONだけではなく、察応するラむブラリの実装です。



この蚘事では、䞀般的なテキスト圢匏パヌサヌ、特にJSONの問題だけでなく、最も負荷の高いプロゞェクトで長幎䜿甚しおきたラむブラリヌに぀いおも説明したす。 速床ず䜿いやすさの䞡方の点で非垞に適しおいるため、より適切な堎合はバむナリ圢匏を拒吊するこずもありたす。 もちろん、私はいく぀かの境界条件を意味したす。







悲劇の芏暡を評䟡するために、よく知られおいるいく぀かのJSONラむブラリgoogle :: protobuf、「手動」JSONシリアル化、およびwjsonラむブラリのあたり耇雑ではないオブゞェクトのシリアル化時間を枬定したした。



結果を図に瀺したす。



私は、これらの結果がやがお、控えめに蚀っおも、少し驚いたこずを認めたす。



jsoncppずjson_spiritboost :: spiritに基づくのパフォヌマンスは、壊滅的にgoogle :: protobufに負けたす。 sprintf / sscanfたたはstd :: stringstreamを䜿甚した「手動」シリアル化の状況は、はるかに優れおいたす。 ただし、最初の2぀のツヌルを䜿甚する堎合は、急いですべおをドロップせず、「自分で䜕をする必芁があるかを䌝えたした」-プロゞェクトをやり盎しおください。 グラフでは、枬定はsprintf / sscanfの1回の呌び出しに察するものであり、JSONオブゞェクトのフィヌルドを䞊べ替えたりスキップしたりするこずなく、シリアル化可胜なオブゞェクトを詰め蟌みたした。 オブゞェクトのシリアル化に関するセクションで、より詳现な数倀を瀺したす。



この蚘事では、JSONをパフォヌマンスに焊点を圓おたメッセヌゞング圢匏ずしお芋おいたす。 したがっお、このコンテキストで特定のテクノロゞヌを比范したす。 たた、開発コンパむル段階でのメッセヌゞ構造がわかっおいるこずも意味したす。 提案されおいるwjsonラむブラリも、これらのタスク専甚に開発されおいたす。 もちろん、未知のJSONドキュメントを䜿甚しお調べるこずができたす。おそらく、wjsonは倚くのラむブラリいずれの堎合もjsoncppずjson_spiritよりも効果的です。



実際、wjsonは、たずえば䞊蚘のラむブラリよりも抂念的にprotobufに近いです。 同様に、メタ蚘述に埓っおシリアル化/逆シリアル化コヌドを生成したす。 しかし、protobufずは異なり、倖郚アプリケヌションではなく、C ++コンパむラを䜿甚したす。 以前の蚘事で、 tic-tac-toeを再生するようコンパむラヌに教える方法を瀺したした。たた、シリアル化コヌドを生成する方法を教えるこずはテクニックの問題です。



しかし、私が最も気に入っおいるのは、シリアラむズ可胜なデヌタ構造に远加機胜を実装する必芁がないこずです。個別にフラむ、個別にカツレツを。 さらに、倚重継承、ネストの集玄、列挙型のシリアル化などのいく぀かの利点を含む、継承がサポヌトされおいたす。



圓初、wjsonは、プログラマが倚くの必芁なチェックを䌎う実行時コヌドを蚘述しないようにするために、c ++テンプレヌトのJSON構造の宣蚀的な蚘述のためだけに考案されたした。 しかし、コンパむラヌが積極的にむンラむン・むットのような構成であるこずがすぐに明らかになりたした。 そしお、これらの蚭蚈を十分に効率的に動䜜させお、蚱容可胜なパフォヌマンスレベルに到達するには、かなりの劎力が必芁でした。



では、なぜJSONはそんなに遅いのでしょうか




XMLを䜿甚した堎合、デシリアラむズには2぀のアプロヌチがあるこずに気づきたす-これらはDOMDocument Object ModelずSAXSimple API for XMLです。 DOMの堎合、テキストはノヌドのツリヌに倉換され、察応するAPIを䜿甚しお調べるこずができたす。 たた、SAXパヌサヌの動䜜は異なりたす。ドキュメントをスキャンし、ナヌザヌコヌドによっお凊理される特定のむベントを生成したす。これは通垞、コヌルバック関数ずしお実装されたす。 䜕らかの方法で、ほずんどのテキストデシリアラむザヌはこれらのアプロヌチのいずれかを䜿甚するか、たたは組み合わせたす。



DOMの䞻な欠点は、垞に高䟡なツリヌを構築する必芁があるこずです。 逆シリアル化の段階でそのようなツリヌを構築するプロセスは、適甚されたアルゎリズムの実行よりもかなり時間がかかる堎合がありたす。 ただし、これに加えお、必芁なフィヌルドを怜玢し、それらを内郚デヌタ構造に倉換する必芁がありたす。 そしお、このタスクはすでにプログラマヌの肩にかかっおおり、非垞に非効率的に実装できたす。 実際、これは䞻芁なリ゜ヌスを食い尜くすのはDOMツリヌの構築であるため、それほど重芁ではありたせん。



SAXパヌサヌははるかに高速です。 抂しお、圌らは察応するハンドラヌを呌び出しお、テキストを䞀床通過したす。 JSONにこのようなパヌサヌを実装するのは簡単な䜜業です。JSON自䜓は䞍名誉に簡単であり、これがその魅力です。 しかし、デヌタを抜出するためにそれを䜿甚するプログラマヌからのより倚くの努力が必芁です。 そしお、これはプログラマにずっおより倚くの䜜業であり、より倚くの゚ラヌず非効率的なコヌドであり、SAXの有効性を無効にする可胜性がありたす。



䞀般的に、効果的なシリアル化のトピックは、普遍的な゜リュヌションを開発するずいう芳点から非垞に興味深いものです。 しかし、応甚プログラミングの芳点から芋るず、これは非垞に退屈で退屈であり、その結果、膚倧な量の効果のないたわごずです。 プログラマがアプリケヌションコヌドを最適化するこずは非垞に興味深いこずです。逆シリアル化は1桁遅く、このコンテキストでの最適化はあたり意味がなく、あたり気にしたせん。



しかし、䜕らかの理由で既存の゜リュヌションのほずんどが私たちに合わない堎合は、手動でやっおみたしょう。JSONの利点は非垞にシンプルなフォヌマットです。 そしお、ここで私は非垞に興味深い話を数回芳察したした。 プログラマがバむナリプロトコルを䜿甚する堎合、ビットを慎重にシフトし、コヌドを最適化しお楜しんでいたす。 しかし、圌が同じコンテキストでテキスト圢匏で䜜業するように申し出られた堎合、䜕かが圌の脳で切断されおいるようです。 より正確には、逆に、圌は高次の自転車の構築に察する保護をオンにしたす。これにより、バむナリ圢匏で䜜業するずきに特に苊劎するこずはありたせんでした。 圌は、JSONで動䜜するコヌドを䜕らかの方法で高貎にするために、ラむブラリのヒヌプを接続しお、いく぀かの矎しいただし、原則ずしおあたり効果的ではない関数のテキストを操䜜したす。 そしお、結果はただ嘆かわしいです。



atoiの実装を曞くこずを考えおいる人はほずんどいたせんが、私たちはただ詊みたす



template<typename T, typename P> P my_atoi( T& v, P beg, P end) { if( beg==end) return end; bool neg = ( *beg=='-' ); if ( neg ) ++beg; if ( beg == end || *beg < '0' || *beg > '9') return 0; if (*beg=='0') return ++beg; v = 0; for ( ;beg!=end; ++beg ) { if (*beg < '0' || *beg > '9') break; v = v*10 + (*beg - '0'); } if (neg) v = static_cast<T>(-v); return beg; }
      
      







実際、すべおはシンプルですが、叀兞的なatoiよりも普遍的で䟿利です私の意芋では。 しかし、最も興味深いのは、2倍の速床で動䜜するこずです。 はい、もちろん、倧郚分はむンラむン眮換のためですが、それはポむントではありたせん。 ちなみに、sscanf / sprintfは、dよりも速くsパラメヌタヌを実行し、同等の行の長さを持ちたす。



sscanf / sprintfの危険性に぀いおは今からお話ししたせん。既に䜕床も曞いおおり、さらに安党な代替手段、たずえばstd :: stringstreamたたはboost :: lexical_cast <>がありたす。 残念ながら、C ++を含む倚くのプログラマヌは、真のCはより高速で、that望の氞続性を備えたsscanf / sprintfを䜿い始めるずいう神話に導かれおいたす。 しかし、この文脈では、問題は蚀語にあるのではなく、1぀たたは別の機胜の実装にありたす。 たずえば、std :: stringstreamを正しく䜿甚するず、Cの遞択肢より悪くなるこずはありたせんが、たずえば、boost :: lexical_cast <>はこの点で著しく劣るこずがありたす。



したがっお、サヌドパヌティのラむブラリだけでなく、䜿い慣れたツヌルのパフォヌマンスに぀いおも慎重にテストする必芁がありたす。 ただし、むンタヌネット䞊の必芁な実装を確認するこずにより、倚くの堎合、サむクルが速くなりたす。



my_atoiのコヌドはwjsonからほが完党に倉曎されおいないため、誰かが圹に立぀かもしれたせん。 シリアル化のコヌドはもう少し混乱しおいたす。



むトア


 //         template<typename T> struct integer_buffer_size { enum { value = sizeof(T)*2 + sizeof(T)/2 + sizeof(T)%2 + is_signed_integer<T>::value }; }; //       template<typename T, int > struct is_signed_integer_base { enum { value = 1 }; static bool is_less_zero(T v) { return v < 0; } }; //    ,  false template<typename T> struct is_signed_integer_base<T, false> { enum { value = 0 }; static bool is_less_zero(T ) { return false; } }; template<typename T> struct is_signed_integer: is_signed_integer_base< T, ( T(-1) < T(1) ) > { }; template<typename T, typename P> P my_itoa(T v, P itr) { char buf[integer_buffer_size<T>::value]; char *beg = buf; char *end = buf; if (v==0) *(end++) = '0'; else { //      if (false) //     .    //      if ( is_signed_integer<T>::is_less_zero(v) ) { for( ; v!=0 ; ++end, v/=10) *end = '0' - v%10; *(end++)='-'; } else { for( ; v!=0 ; ++end, v/=10) *end = '0' + v%10; } } do { *(itr++)=*(--end); } while( end != beg ); return itr; }
      
      









JSONの残りの郚分およびむンラむン眮換の構成芁玠に察するこのようなバむトごずの䞊べ替えにより、より高速な逆シリアル化を実珟できたす。 䜕らかの方法でそれらを1぀のデザむンにアセンブルするず、䞀皮のSAXパヌサヌが埗られたすが、これも非垞に高速です。





単玔型





シリアル化の䟋をすぐに芋おみたしょう。



 int value = 12345; char bufjson[100]; char* ptr = wjson::value<int>::serializer()(value, bufjson); *ptr = '\0'; std::cout << bufjson << std::endl;
      
      







ここで、wjson :: value <int>は、この型のシリアラむザヌ定矩を含む敎数型のJSON蚘述です。 次に、シリアラむザヌオブゞェクトを䜜成し、オヌバヌロヌドされた挔算子を呌び出したす。 䞀郚の人にずっおは、そのような゚ントリは奇劙に芋えるかもしれたせんが、JSONシリアラむザオブゞェクトには状態がなく、そのむンスタンスを䜜成する意味がないこずを匷調するために䜿甚したす。



シリアラむザが静的関数ではない理由の質問にすぐに答えたす。 第䞀に、コンパむラヌはコンパむル時間の点で静的芁玠を本圓に奜きではありたせん。第二に、ずにかく、私にずっおより䟿利です。 実際、䞊蚘のネタバレの䞋に瀺したコヌドは、my_itoaを䟋に䜿甚しお完党に眮き換えられたす。



倀<>コンストラクトは、敎数だけでなく、実際のもの、文字列、およびブヌル倀にも䜿甚されたす。 定矩

 template<typename T, int R = -1> struct value { typedef T target; typedef implementation_defined serializer; };
      
      





ブヌル型および敎数型の堎合、匕数Rは䜿甚されたせん。 std :: stringたたはstd :: vector <char>などの文字列の堎合、これは予玄のサむズであり、実際の文字列の堎合は衚瀺圢匏です。



シリアラむザヌクラスは、シリアル化に加えお、逆シリアル化機胜を提䟛したす。 2぀のオヌバヌロヌド挔算子

 template<typename T> class implementation_defined { public: template<typename P> P operator()( const T& v, P itr); template<typename P> P operator() ( T& v, P beg, P end, json_error* e ); };
      
      





シリアル化関数は、シリアル化可胜な型ぞの参照に加えお、たずえば次のような出力反埩子を受け入れたす。

 int value = 12345; std::string strjson; wjson::value<int>::serializer()(value, std::back_inserter(strjson)); std::cout << strjson << std::endl; std::stringstream ssjson; wjson::value<int>::serializer()(value, std::ostreambuf_iterator<char>(ssjson)); std::cout << ssjson.str() << std::endl; wjson::value<int>::serializer()(value, std::ostreambuf_iterator<char>(std::cout)); std::cout << std::endl;
      
      





デシリアラむザは、入力ぞのランダムアクセスむテレヌタを受け入れ、バッファの開始ず終了を瀺すずずもに、゚ラヌオブゞェクトぞのポむンタれロの堎合もあるを瀺したす。

 value = 0; char bufjson[100]=”12345”; wjson::value<int>::serializer()(value, bufjson, bufjson + strlen(bufjson), 0 ); std::cout << value << std::endl; value = 0; std::string strjson=”12345”; wjson::value<int>::serializer()(value, strjson.begin(), strjson.end(), 0 ); std::cout << value << std::endl;
      
      





シリアラむザヌずデシリアラむザヌの䞡方がむテレヌタヌを返したす。 最初のケヌスでは、シリアル化が終了した堎所を瀺し、2番目のケヌスでは、゚ラヌがなければ、シリアル化が終了した入力バッファ内の堎所を瀺したす。 ゚ラヌが発生した堎合、バッファの末尟ぞのポむンタを返したす。null以倖のポむンタが枡された堎合ぱラヌコヌドを返したす。 少し埌で゚ラヌ凊理に぀いお説明したす。そしお、単玔な型を完成させたす。



サポヌトされる敎数char、unsigned char、short、unsigned short、int、unsigned int、long int、unsigned long、long long、unsigned long long。 ブヌル倀boolを䜿甚するず、すべおが同じであり、「true」たたは「false」にシリアル化されたす。 デシリアラむズ䞭の他のタむプからの自動倉換はサポヌトされおいたせん。



パフォヌマンスに関しお特に気にしなかった唯䞀のタむプは実際のタむプfloat、double、long doubleで、通垞のstd :: stringstreamがありたす。 これは䞻に、実際に䜜業したプロゞェクトで、敎数型ミリメヌトル単䜍の転送メヌタヌなど、たたはCPUコアごずに10K以内の負荷に眮き換えるこずができるずいう事実によるものです。これは重芁ではありたせん。 トラフィックの倧郚分がある堎合-それが珟実であり、どのような方法でもそこから逃れるこずができない堎合、最適化ず混同するこずは理にかなっおいたす。 デフォルトでは、実際のものは仮数でシリアル化されたす。 R> = 0の堎合、固定コンマの堎合

 double value = 12345.12345; std::string json; wjson::value<double>::serializer()(value, std::back_inserter(json)); std::cout << json << std::endl; json.clear(); wjson::value<double, 4>::serializer()(value, std::back_inserter(json)); std::cout << json << std::endl;
      
      







結果

 1.234512e+04 12345.1234
      
      





utf-8を䜿甚する堎合、䞀芋するずすべおが文字列でシンプルになりたすが、次の点に泚意する必芁がありたす。





䞀郚のサヌドパヌティラむブラリは、特に負担をかけるこずなく、ASCII範囲コヌド> 127以倖のすべおを\ uXXXX圢匏でシリアル化したす。 ただし、wjsonを䜿甚しお同様の文字列を逆シリアル化するず、utf-8でデコヌドされたす。 wjsonが再シリアル化されるず、この゚スケヌプ凊理はなくなりたす。



通垞、゜フトりェア゚ラヌが原因で、行の䞭倮が '\ 0'であり、wjsonを含むほずんどのシリアラむザヌによっお\ u0000に倉換されたすが、逆シリアル化された堎合、\ 0に倉換されず、そのたた残りたす。



\ xXX圢匏のサポヌトは、無効なデヌタシリアル化たたはコンパむルされおいないを意味しないwjsonシリアル化の抂念の制限によっおのみ決定されたす。 バむナリデヌタをシリアル化するには、たずえばBase64を䜿甚したす。



文字列のシリアル化の䟋


 #include <wjson/json.hpp> #include <iostream> #include <cstring> int main() { const char* english = "\"hello world!\""; const char* russian = "\"\\u041F\\u0440\\u0438\\u0432\\u0435\\u0442\\u0020\\u043C\\u0438\\u0440\\u0021\""; const char* chinese = "\"\\u4E16\\u754C\\u4F60\\u597D!\""; typedef char str_t[128]; typedef wjson::value< std::string, 128 >::serializer sser_t; typedef wjson::value< std::vector<char> >::serializer vser_t; typedef wjson::value< str_t >::serializer aser_t; std::string sstr; std::vector<char> vstr; str_t astr={'\0'}; //  sser_t()( sstr, english, english + std::strlen(english), 0); vser_t()( vstr, russian, russian + std::strlen(russian), 0); aser_t()( astr, chinese, chinese + std::strlen(chinese), 0); //  std::cout << "English: " << sstr << "\tfrom JSON: " << english << std::endl; std::cout << "Russian: " << std::string(vstr.begin(), vstr.end() ) << "\tfrom JSON: " << russian << std::endl; std::cout << "Chinese: " << astr << "\tfrom JSON: " << chinese << std::endl; //  english  stdout std::cout << std::endl << "English JSON: "; sser_t()( sstr, std::ostream_iterator<char>( std::cout) ); std::cout << "\tfrom: " << sstr; //  russian  stdout std::cout << std::endl << "Russian JSON: "; vser_t()( vstr, std::ostream_iterator<char>( std::cout) ); std::cout << "\tfrom: " << std::string(vstr.begin(), vstr.end() ); //  chinese  stdout std::cout << std::endl << "Chinese JSON: "; aser_t()( astr, std::ostream_iterator<char>( std::cout) ); std::cout << "\tfrom: " << astr; std::cout << std::endl; }
      
      







結果

English: hello world! from JSON: "hello world!"

Russian: ! from JSON: "\u041F\u0440\u0438\u0432\u0435\u0442\u0020\u043C\u0438\u0440\u0021"

Chinese: 䞖界䜠奜! from JSON: "\u4E16\u754C\u4F60\u597D!"



English JSON: "hello world!" from: hello world!

Russian JSON: " !" from: !

Chinese JSON: "䞖界䜠奜!" from: 䞖界䜠奜!









配列



JSON配列を蚘述するために、wjson :: valueに䌌た構造が䜿甚されたす。

 template<typename T, int R = -1> struct array { typedef T target; typedef implementation_defined serializer; };
      
      





ここで、Tはシリアル化可胜なコンテナヌであり、Rはこのメ゜ッドをサポヌトするstlコンテナヌの予玄のサむズです。 すべおが単玔に思えたすが、次の圢匏のレコヌドwjson :: array <std :: vector <int >>は機胜したせん。 コンテナ芁玠この堎合はintをシリアル化する方法はわかりたせん。 正しい゚ントリは次のようになりたす。

 typedef wjson::array< std::vector< wjson::value<int> > > vint_json;
      
      





パラメヌタヌTずしお、必芁なコンテナヌを枡したすが、コンテナヌ芁玠のタむプの代わりに、JSON蚘述を枡したす。 以䞋がサポヌトされおいたす。



もちろん、最初の4぀のオプションは最倧のパフォヌマンスを提䟛したす。 リストず連想コンテナの入力は、それ自䜓が高すぎたす。

叀兞的なs-arrayの䟋

 typedef wjson::value<int> int_json; typedef int vint_t[3]; typedef wjson::array< int_json[3] > vint_json;
      
      





もちろん、次の䟋に瀺すように、倚次元配列がサポヌトされおいたすたずえば、ベクトルのベクトルなど。



ベクトルベクトルの䟋


 #include <wjson/json.hpp> #include <wjson/strerror.hpp> #include <iostream> int main() { //   typedef wjson::value<int> int_json; typedef std::vector<int> vint_t; typedef wjson::array< std::vector<int_json> > vint_json; std::string json="[ 1,\t2,\r3,\n4, /**/ 5 ]"; vint_t vint; vint_json::serializer()(vint, json.begin(), json.end(), NULL); json.clear(); vint_json::serializer()(vint, std::back_inserter(json)); std::cout << json << std::endl; //   (  ) typedef std::vector< vint_t > vvint_t; typedef wjson::array< std::vector<vint_json> > vvint_json; json="[ [], [1], [2, 3], [4, 5, 6] ]"; vvint_t vvint; vvint_json::serializer()(vvint, json.begin(), json.end(), NULL); json.clear(); vvint_json::serializer()(vvint, std::back_inserter(json)); std::cout << json << std::endl; //   (   ) typedef std::vector< vvint_t > vvvint_t; typedef wjson::array< std::vector<vvint_json> > vvvint_json; json="[ [[]], [[1]], [[2], [3]], [[4], [5, 6] ] ]"; vvvint_t vvvint; vvvint_json::serializer()(vvvint, json.begin(), json.end(), NULL); json.clear(); vvvint_json::serializer()(vvvint, std::back_inserter(json)); std::cout << json << std::endl; }
      
      







ここでは、JSON文字列を取埗し、それをコンテナにデシリアラむズし、クリアし、同じ文字列ず出力にシリアル化したす。

 [1,2,3,4,5] [[],[1],[2,3],[4,5,6]] [[[]],[[1]],[[2],[3]],[[4],[5,6]]]
      
      





json行は、ラむンフィヌドやCスタむルのコメントなど、配列の芁玠間に空癜が存圚する可胜性があるこずを瀺しおいたす。これはjson構成を実装するずきに非垞に䟿利です。



動的コンテナの最倧サむズに制限はありたせん。s-arraysおよびstd :: arrayの堎合、配列の実際のサむズは制限です。 着信JSON芁玠の数が少ない堎合、残りの芁玠にはデフォルト倀が入力され、それ以䞊の堎合、䜙分な芁玠は単玔に砎棄されたす。



JSON配列に異なるタむプの芁玠が含たれる堎合


JSON配列にさたざたなタむプの芁玠が含たれる堎合、それらは2段階でシリアル化および非シリアル化されたす。 たず、次のように、任意の非シリアル化されおいないJSON構造を含む文字列のコンテナを蚘述する必芁がありたす。

 typedef std::vector<std::string> vstr;
      
      





生のJSONを蚘述するには

 template<typename T = std::string, int R = -1> struct raw_value;
      
      





JSON文字列をそのたたコンテナTにコピヌしたす。そしお、パヌサヌを䜿甚しお、JSON芁玠のタむプを刀別し、それに応じおデシリアラむズする必芁がありたす。 以䞋の䟋では、数倀の配列[1、 "2"、[3]]を読み取っお、すべおの芁玠をむンクリメントし、フォヌマットを維持しながらそれをシリアル化しようずしおいたす。



コヌド


 #include <wjson/json.hpp> #include <wjson/strerror.hpp> #include <iostream> int main() { typedef std::vector< std::string > vect_t; typedef ::wjson::array< std::vector< ::wjson::raw_value<std::string> > > vect_json; vect_t inv; vect_t outv; std::string json = "[1,\"2\",[3]]"; std::cout << json << std::endl; vect_json::serializer()( inv, json.begin(), json.end(), 0 ); for ( auto& v : inv ) { outv.push_back(""); if ( wjson::parser::is_number(v.begin(), v.end()) ) { int num = 0; wjson::value<int>::serializer()( num, v.begin(), v.end(), 0); ++num; wjson::value<int>::serializer()( num, std::back_inserter(outv.back()) ); } else if ( wjson::parser::is_string(v.begin(), v.end()) ) { std::string snum; wjson::value<std::string>::serializer()( snum, v.begin(), v.end(), 0); int num = 0; wjson::value<int>::serializer()( num, snum.begin(), snum.end(), 0); ++num; snum.clear(); wjson::value<int>::serializer()( num, std::back_inserter(snum) ); wjson::value<std::string>::serializer()( snum, std::back_inserter(outv.back()) ); } else if ( wjson::parser::is_array(v.begin(), v.end()) ) { std::vector<int> vnum; wjson::array< std::vector< wjson::value<int> > >::serializer()( vnum, v.begin(), v.end(), 0); ++vnum[0]; wjson::array< std::vector< wjson::value<int> > >::serializer()( vnum, std::back_inserter(outv.back()) ); } else { outv.back()="null"; } } json.clear(); vect_json::serializer()( outv, std::back_inserter(json) ); std::cout << json << std::endl; }
      
      







結果

 [1,"2",[3]] [2,"3",[4]]
      
      





これは、埌で説明するオブゞェクトず蟞曞でも機胜したす。 数字を文字列たたは実際には数字の2぀のオプションでしか衚珟できない堎合は、ラッパヌを䜿甚できたす。

 template<typename J, bool SerQ = true, bool ReqQ = true, int R = -1> struct quoted;
      
      





  • J-゜ヌスJSONの説明
  • SerQ-文字列に事前シリアル化
  • ReqQ-入力JSONは「文字列」でなければなりたせん
  • 䞭間バッファヌ行のR予玄


このコンストラクトは、実際にはどのJSON蚘述でも機胜したす。 SerQパラメヌタヌは、デュアルシリアル化を有効にしたす。 たずえば、数倀の堎合、これは単に匕甚笊を意味したす。 ReqQパラメヌタヌには、二重逆シリアル化が含たれたす。 入力にJSON文字列が必芁です。 オフの堎合、ルヌルはもう少し耇雑です。 入力がJSON文字列でない堎合は、事前の逆シリアル化を行わずに、Jデシリアラむザヌを単に開始したす。 入力がJSON文字列の堎合、䞭間のstd ::文字列に逆シリアル化されたす。 Jが非文字列゚ンティティを蚘述する堎合、䞭間std ::文字列からの再シリアル化解陀。 文字列゚ンティティの堎合、逆シリアル化を繰り返す必芁があるず刀断したす。 これは、最初の逆シリアル化の埌、䞭間行が匕甚笊で始たる堎合、これは二重シリアル化された行であり、再び逆シリアル化されるこずを意味したす。



wjson :: quoted <>は远加のオヌバヌヘッドを䞎えるこずは明らかであり、䜕らかの理由で、クラむアントが期限たでに番号を「だたし」、シリアル化するか、ネストされたオブゞェクトの二重シリアル化を行う堎合、䞀時的な束葉杖ず芋なされる必芁がありたす。





パヌサヌ



wjsonには、2぀のタむプに分類できる静的メ゜ッドのみを含むパヌサヌクラスがありたす。 これは特定のJSONタむプぞの準拠のチェックであり、したがっお、メ゜ッドはパヌサヌです。 各JSONタむプには独自のメ゜ッドがありたす。



方匏リスト


 class parser { /*...*/ public: template<typename P> static P parse_space( P beg, P end, json_error* e); template<typename P> static P parse_null( P beg, P end, json_error* e ); template<typename P> static P parse_bool( P beg, P end, json_error* e ); template<typename P> static P parse_number( P beg, P end, json_error* e ); template<typename P> static P parse_string( P beg, P end, json_error* e ); template<typename P> static P parse_object( P beg, P end, json_error* e ); template<typename P> static P parse_array( P beg, P end, json_error* e ); template<typename P> static P parse_value( P beg, P end, json_error* e ); /*...*/ };
      
      







デシリアラむザヌず同様に、ここでのbegはバッファヌの始たり、バッファヌの終わり、およびnullptrでない堎合は「e」で゚ラヌコヌドが曞き蟌たれたす。 成功した堎合、珟圚の゚ンティティの最埌の文字に続く文字ぞのポむンタが返されたす。 ゚ラヌが発生した堎合、endが返され、eが初期化されたす。



特定の構造の耇数のJSONオブゞェクトを含む文字列があり、それらが改行たたは他の空癜で区切られおいる堎合、次のように凊理できたす゚ラヌ凊理なし。

 for (;beg!=end;) { beg = wjson::parser::parse_space(beg, end, 0); beg=my_json::serializer()(dict, beg, end, 0); /* 
. */ }
      
      





すべおのシリアラむザは、最初の文字がデシリアラむズ可胜なオブゞェクトの文字でなければならないこずを前提ずしおいたす。そうしないず゚ラヌが発生したす。 しかし、すでに述べたように、オブゞェクトず配列の内郚には、デシリアラむザヌが同じparse_spaceで解析するコメントを含む空癜文字が含たれる堎合がありたす。 耇数のJSON゚ンティティで文字列を解析する䟋

 wjson::json_error e; for (;beg!=end;) { beg = wjson::parser::parse_space(beg, end, &e); beg = wjson::parser::parse_value(beg, end, &e); if ( e ) abort(); }
      
      





ここで、parse_valueはJSON゚ンティティの劥圓性をチェックしたす。 入力parse_spaceが空癜でない堎合、単玔にbegを返したす。 たずえば、閉じられおいないCスタむルのコメントが芋぀かった堎合、゚ラヌを返すこずがありたすが、ここでは远加のチェックは䞍芁です。 初期化された゚ラヌオブゞェクトがパヌサヌぞの入力に到着した堎合デシリアラむザヌず同様、単にendを返したす。



特定のJSON゚ンティティを定矩するには、次のメ゜ッドセットがありたす。



方匏リスト


 class parser { /*...*/ public: template<typename P> static bool is_space( P beg, P end ); template<typename P> static bool is_null( P beg, P end ); template<typename P> static bool is_bool( P beg, P end ); template<typename P> static bool is_number( P beg, P end ); template<typename P> static bool is_string( P beg, P end ); template<typename P> static bool is_object( P beg, P end ); template<typename P> static bool is_array( P beg, P end ); };
      
      







バッファの先頭ず末尟ぞのポむンタを取埗するずいう事実にもかかわらず、これらのメ゜ッドは最初の文字で゚ンティティを決定したす。{はオブゞェクト、[は配列、 "は文字列、数字は数字、t、fたたはnはtrue 、falseたたはnull。 したがっお、たずえばis_objectがtrueを返す堎合、これが有効なオブゞェクトであるこずを確認するには、parse_objectを呌び出しお゚ラヌがないこずを確認する必芁がありたす。



゚ラヌ凊理



逆シリアル化䞭の゚ラヌチェックはほずんどの堎合必芁です。 䟋では、明確にするためだけにこれを行いたせん。 倖郚の文字が元の配列に埋め蟌たれおいる䟋を考えおみたしょう。

 #include <wjson/json.hpp> #include <wjson/strerror.hpp> #include <iostream> int main() { typedef wjson::array< std::vector< wjson::value<int> > >::serializer serializer_t; std::vector< int > value; std::string json = "[1,2,3}5,6]"; wjson::json_error e; serializer_t()(value, json.begin(), json.end(), &e ); if ( e ) { std::cout << "Error code: " << e.code() << std::endl; std::cout << "Error tail of: " << e.tail_of() << std::endl; if ( e.type() == wjson::error_code::ExpectedOf ) std::cout << "Error expected_of: " << e.expected_of() << std::endl; std::cout << "Error position: " << wjson::strerror::where(e, json.begin(), json.end() ) << std::endl; std::cout << "Error message: " << wjson::strerror::message(e) << std::endl; std::cout << "Error trace: " << wjson::strerror::trace(e, json.begin(), json.end()) << std::endl; std::cout << "Error message & trace: " << wjson::strerror::message_trace(e, json.begin(), json.end()) << std::endl; } }
      
      





実際には、wjson :: json_error゚ラヌオブゞェクト自䜓には、゚ラヌコヌドに関する情報ず、パヌサヌが䞍敎合を怜出したバッファヌの末尟に察する䜍眮が含たれおいたす。 特別なタむプ「Expected of」の゚ラヌの堎合、圌が期埅したキャラクタヌ。



読み取り可胜なメッセヌゞを取埗するには、wjson :: strerrorクラスを䜿甚したす。 䞊蚘の䟋では、JSON配列で}文字が怜出され、パヌサヌはコンマりェル、たたは角括匧を予期し、それを報告したす。 この䟋は、゚ラヌ分析に䜿甚可胜なすべおのメ゜ッドを瀺しおいたす。 結果は次のずおりです。

  Error code: 3 Error tail of: 5 Error expected_of: , Error position: 6 Error message: Expected Of ',' Error trace: [1,2,3>>>}5,6] Error message & trace: Expected Of ',': [1,2,3>>>}5,6]
      
      





したがっお、゚ラヌコヌド、読み取り可胜なメッセヌゞだけでなく、それが発生した堎所も取埗できたす。トレヌス時には、「>>>」の組み合わせが䜿甚されたす。



JSONオブゞェクト



JSONオブゞェクトをデヌタ構造に盎接デシリアラむズするこずが、wjsonの蚭蚈目的です。単玔な構造を考えおみたしょう。

 struct foo { bool flag = false; int value = 0; std::string string; };
      
      





JSONタむプでシリアル化する必芁がありたす

 { "flag":true, "value":42, "string":" !"}
      
      





JSONオブゞェクトは、コロンで区切られた名前ず倀任意のJSONで構成されるフィヌルドのリストの単なる列挙です。単䞀のフィヌルドをシリアル化するには、コンパむル段階で既知の名前をコピヌし、コロンを远加しお、倀をシリアル化する必芁がありたす。蚭蚈はこの抂念を実装したす。

 template<typename N, typename T, typename M, MT::* m, typename J = value<M> > struct member;
      
      







ただし、テンプレヌトパラメヌタを䜿甚しお文字列を明瀺的に枡すこずには問題がありたす。したがっお、次のトリックを䜿甚したす。構造フィヌルド名ごずに、次の圢匏の構造を䜜成したす。



旗の名前


 struct n_flag { const char* operator()() const { return “flag”; } };
      
      







テンプレヌトパラメヌタで枡すこずができたす。もちろん、名前ごずにこのような構造を䜜成するこずはあたり䟿利ではないため、たれにマクロ眮換を蚱可した堎合がありたす。これを行うには、マクロを䜿甚できたす。

 JSON_NAME(flag)
      
      





ほが同じ構造が䜜成されたす。n_プレフィックスは歎史的な理由で䜿甚されたす。ただし、気に入らない堎合は、2番目のオプションを䜿甚できたす。

 JSON_NAME2(n_flag, “flag”)
      
      





これにより、任意の名前ず文字列で構造䜓を䜜成できたす。単䞀のフィヌルドを説明する䟋

 wjson::member< n_flag, foo, bool, &foo::flag>
      
      





単玔型の堎合、JSONの説明wjson :: value <>は省略できたすが、他のすべおの堎合は必須です。構造䜓フィヌルド自䜓のシリアル化はあたり意味がないため、次のようにすべおのフィヌルドの説明をリストにたずめる必芁がありたす。

 wjson::member_list< wjson::member<n_flag, foo, bool, &foo::flag>, wjson::member<n_value, foo, int, &foo::value>, wjson::member<n_string, foo, std::string, &foo::string> >
      
      





C ++ 11の堎合、フィヌルドの数は制限されたせん。c++ 03の堎合、26個の芁玠の制限があり、ネストされたmember_listを䜿甚しお簡単に回避できたす。JSONオブゞェクトを構造にシリアル化するための芏則は、次の構成によっお提䟛されたす。

 template<typename T, typename L> struct object { typedef T target; typedef implementation_defined serializer; typedef implementation_defined member_list; };
      
      





ここで、Tはデヌタ構造のタむプ、Lはシリアル化可胜なフィヌルドのリストmember_listです。



JSONオブゞェクトのシリアラむズおよびデシリアラむズの䟋


 #include <wjson/json.hpp> #include <wjson/strerror.hpp> #include <iostream> struct foo { bool flag = false; int value = 0; std::string string; }; JSON_NAME(flag) JSON_NAME(value) JSON_NAME(string) typedef wjson::object< foo, wjson::member_list< wjson::member<n_flag, foo,bool, &foo::flag>, wjson::member<n_value, foo,int, &foo::value>, wjson::member<n_string, foo,std::string, &foo::string> > > foo_json; int main() { std::string json="{\"flag\":false,\"value\":0,\"string\":\" \"}"; foo f; foo_json::serializer()( f, json.begin(), json.end(), nullptr ); f.flag = true; f.string = " "; std::cout << json << std::endl; foo_json::serializer()( f, std::ostream_iterator<char>(std::cout) ); }
      
      







結果

 {"flag":false,"value":0,"string":" "} {"flag":true,"value":0,"string":" "}
      
      





私があなたに泚意を匕きたいのは



入力JSONでフィヌルドの順序がJSON蚘述の順序ず䞀臎する堎合、実際には1回のパスで逆シリアル化が可胜な限り迅速に行われたす。䞍足しおいるフィヌルドやパフォヌマンス䞊の䜙分な芁玠は、倧きな圱響を䞎えたせんそれらは単に無芖されたす。



しかし、JSONのフィヌルドがランダムな順序で来るずどうなりたすかもちろん、これはパフォヌマンスに圱響したす。これは、パヌサヌが混乱し、最初にフィヌルドを反埩凊理するためです。しかし、フィヌルドオヌダヌのトピックをたったく気にしないこずをお勧めしたす。



実際に感じ始める前であっおも、デシリアラむズの時間ではなく、JSONの冗長性の問題に遭遇し、デヌタ亀換フォヌマットの倉曎に぀いお考える必芁がありたす。これは、必ずしもバむナリプロトコルに切り替えるこずを意味したせん。たずえば、JSON配列の圢匏でオブゞェクトを転送するこずができたす。この堎合、䜍眮は構造の特定のフィヌルドに厳密に察応したす。特別な堎合、倚くのれロが送信される堎合、そのような圢匏はprotobufよりもコンパクトで高速になりたす。



根拠にならないように、パフォヌマンスのために次の構造デシリアラむれヌションを実行したした。

 struct foo { int field1 = 0; int field2 = 0; int field3 = 0; std::vector<int> field5; };
      
      





fooのJSON蚘述


  JSON_NAME(field1) JSON_NAME(field2) JSON_NAME(field3) JSON_NAME(field5) typedef wjson::object< foo, wjson::member_list< wjson::member<n_field1, foo, int, &foo::field1>, wjson::member<n_field2, foo, int, &foo::field2>, wjson::member<n_field3, foo, int, &foo::field3>, wjson::member<n_field5, foo, std::vector<int>, &foo::field5, ::wjson::array< std::vector< ::wjson::value<int> > > > > > foo_json;
      
      







入力JSONのフィヌルドの順方向および逆方向最も倱敗したシヌケンス。しかし、その埌、圌はフィヌルド名が完党に正盎ではないこずに気づきたした、最埌の文字の䟋倖ず䞀臎するため、オプションの枬定も行いたした。

  JSON_NAME2(n_field1, "1field") JSON_NAME2(n_field2, "2field") JSON_NAME2(n_field3, "3field") JSON_NAME2(n_field5, "5field")
      
      





すべおのフィヌルドが最初の文字で区別される堎合。JSONの芁玄

 {"field1":12345,"field2":23456,"field3":34567,"field5":[45678,56789,67890,78901,89012]} {"5field":[45678,56789,67890,78901,89012],"1field":12345,"2field":23456,"3field":34567} {"field5":[45678,56789,67890,78901,89012],"field1":12345,"field2":23456,"field3":34567}
      
      





次の結果を埗たした



明確にするために、たた蚘事の冒頭で取り䞊げたトピックsprintf / sscanfを閉じるために、このような構成の実行時間も枬定したした。

 sscanf( str, "{\"field1\":%d,\"field2\":%d,\"field3\":%d,\"field5\":[%d,%d,%d,%d,%d]}", &(f.field1), &(f.field2), &(f.field3), &(f.field5[0]), &(f.field5[1]), &(f.field5[2]),&(f.field5[3]), &(f.field5[4]) );
      
      





ここでは、本栌的なデシリアラむれヌションの話ができないこずは明らかです-パタヌンずの矛盟は悲惚な結果に぀ながる可胜性がありたす。ただし、結果は2477942 ns403560 persecであり、これはすべおのチェックでwjsonよりも10倍悪く、「悪い」順序ず「良くない」フィヌルド名で



目を信じおいなかった、確認したい人のためにこれらの数字称賛に倀する、蚘事を読むこずなくこれは歓迎しにくい、これは最適化がオンになっおいる堎合にのみ機胜するこずをすぐに譊告したす。デバッグモヌドでは、数倀をたったく逆に、さらに悪い倀に倉曎したす。垞に䜕かを犠牲にしなければなりたせん。



このような゚ンティティのシリアル化の速床に関する質問は数幎前からありたしたが、JSONの冗長性の問題が時々浮䞊したす。この問題を解決するために、むンデックスにフィヌルドが厳密に関連付けられおいる配列に構造䜓をシリアル化するこずができたす。

  typedef wjson::object_array< foo, wjson::member_list< wjson::member_array<foo, int, &foo::field1>, wjson::member_array<foo, int, &foo::field2>, wjson::member_array<foo, int, &foo::field3>, wjson::member_array<foo, std::vector<int>, &foo::field5, ::wjson::array< std::vector< ::wjson::value<int> > > > > > foo_json;
      
      





その結果、同じ構造䜓が配列にシリアル化されたす。

 [12345,23456,34567,[45678,56789,67890,78901,89012]]
      
      





139856 ns7150211 persecで、131282 nsでデシリアラむれヌションが発生したす7617190



はい、速床に違いはありたすが、たずはよりコンパクトです。サヌバヌがグラフ䜜成甚のデヌタを返すプロゞェクトの1぀では、各ポむントは8぀のフィヌルドで蚘述され、各グラフで玄3000であり、グラフ自䜓は画面䞊で数十になるこずがあり、結果のJSONは数メガバむトになる可胜性がありたす。元の構造を8぀の芁玠の配列にシリアル化するこずにより、送信されるトラフィックの量を倧幅に削枛するだけでなく、読みやすくしたす。しかし、䞀般に、もちろん、すべおのAPIを配列に倉換するこずはお勧めできたせん。



継承





䟋ずしお次の構造を䜿甚しお継承を怜蚎したす。

 struct foo { bool flag = false; int value = 0; std::string string; }; struct bar: foo { std::vector<int> data; };
      
      





継承を蚘述する方法は2぀ありたす。

 typedef ::wjson::array< std::vector< ::wjson::value<int> > > vint_json; typedef wjson::object< bar, wjson::member_list< wjson::member<n_flag, foo,bool, &foo::flag>, wjson::member<n_value, foo,int, &foo::value>, wjson::member<n_string, foo,std::string, &foo::string>, wjson::member<n_data, bar, std::vector<int>, &bar::data, vint_json> > > bar_json;
      
      





ここで、芪たたは芪ず盞続人のフィヌルドを任意の順序で配眮できたす。オプション2、より盎感的

 typedef wjson::object< foo, wjson::member_list< wjson::member<n_flag, foo,bool, &foo::flag>, wjson::member<n_value, foo,int, &foo::value>, wjson::member<n_string, foo,std::string, &foo::string> > > foo_json; typedef wjson::object< bar, wjson::member_list< wjson::base<foo_json>, wjson::member<n_data, bar, std::vector<int>, &bar::data, vint_json> > > bar_json;
      
      





基本クラスの個別のJSON蚘述を䜜成し、リスト内の任意の堎所でfoo_json :: member_listの゚むリアスであるwjson :: base <foo_json>構成を䜿甚しお実装したす。



すべおの芁玠を持぀玠晎らしい䟋。


 #include <wjson/json.hpp> #include <wjson/strerror.hpp> #include <iostream> struct foo { bool flag = false; int value = 0; std::string string; }; struct bar: foo { std::shared_ptr<foo> pfoo; std::vector<foo> vfoo; }; struct foo_json { JSON_NAME(flag) JSON_NAME(value) JSON_NAME(string) typedef wjson::object< foo, wjson::member_list< wjson::member<n_flag, foo,bool, &foo::flag>, wjson::member<n_value, foo,int, &foo::value>, wjson::member<n_string, foo,std::string, &foo::string> > > type; typedef type::serializer serializer; typedef type::target target; typedef type::member_list member_list; }; struct bar_json { JSON_NAME(pfoo) JSON_NAME(vfoo) typedef wjson::array< std::vector< foo_json > > vfoo_json; typedef wjson::pointer< std::shared_ptr<foo>, foo_json > pfoo_json; typedef wjson::object< bar, wjson::member_list< wjson::base<foo_json>, wjson::member<n_vfoo, bar, std::vector<foo>, &bar::vfoo, vfoo_json>, wjson::member<n_pfoo, bar, std::shared_ptr<foo>, &bar::pfoo, pfoo_json> > > type; typedef type::serializer serializer; typedef type::target target; typedef type::member_list member_list; }; int main() { std::string json="{\"flag\":true,\"value\":0,\"string\":\" \",\"vfoo\":[],\"pfoo\":null}"; bar b; bar_json::serializer()( b, json.begin(), json.end(), nullptr ); b.flag = true; b.vfoo.push_back( static_cast<const foo&>(b)); b.pfoo = std::make_shared<foo>(static_cast<const foo&>(b)); std::cout << json << std::endl; bar_json::serializer()(b, std::ostream_iterator<char>(std::cout) ); std::cout << std::endl; }
      
      







結果

 {"flag":true,"value":0,"string":" ","vfoo":[],"pfoo":null} {"flag":true,"value":0,"string":" ","vfoo":[{"flag":true,"value":0,"string":" "}],"pfoo":{"flag":true,"value":0,"string":" "}}
      
      





JSONオブゞェクトの説明は少し異なりたすが、最初はポむンタヌに぀いおです。任意のポむンタヌをシリアル化できたす。れロの堎合、nullずしおシリアル化され、そうでない堎合は倀によっおシリアル化されたす。たた、逆シリアル化はstd :: shared_ptr <>に察しおのみ実装されたす。JSONがnullの堎合、nullptrになりたす。それ以倖の堎合、オブゞェクトが䜜成され、逆シリアル化が発生したす。wjson :: pointerずしお蚘述しなかった芁玠に぀いおは、nullが入力に到着するず、デフォルト倀で䜜成されたす。これは、配列ず単玔型にも適甚されたす。

倚数のパラメヌタヌを持぀スクリヌンテンプレヌトクラスを遞択する理由


JSON- , , . , , . , :

 template<typename J> struct deserealizer { typedef typename J::deserializer type; };
      
      





:

 typedef deserealizer<bar_json>::type deser;
      
      





なぜなら deserializer , :

 error: no type named 'deserializer' in 'struct bar_json'
      
      





foo_json bar_json typedef, :

 error: no type named 'deserializer' in 'struct wjson::object<bar, fas::type_list<wjson::member<n_flag, foo, bool, &foo::flag>, fas::type_list<wjson::member<n_value, foo, int, &foo::value>, fas::type_list<wjson::member<n_string, foo, std::basic_string<char>, &foo::string>, fas::type_list<wjson::member<n_vfoo, bar, std::vector<foo>, &bar::vfoo, wjson::array<std::vector<wjson::object<foo, fas::type_list<wjson::member<n_flag, foo, bool, &foo::flag>, fas::type_list<wjson::member<n_value, foo, int, &foo::value>, fas::type_list<wjson::member<n_string, foo, std::basic_string<char>, &foo::string>, fas::empty_list> > > > > > >, fas::type_list<wjson::member<n_pfoo, bar, std::shared_ptr<foo>, &bar::pfoo, wjson::pointer<std::shared_ptr<foo>, wjson::object<foo, fas::type_list<wjson::member<n_flag, foo, bool, &foo::flag>, fas::type_list<wjson::member<n_value, foo, int, &foo::value>, fas::type_list<wjson::member<n_string, foo, std::basic_string<char>, &foo::string>, fas::empty_list> > > > > >, fas::empty_list> > > > > >'
      
      





, . , , ., , typeid(T).name(). , , , , . , , :

 template<typename J> struct deserealizer { struct type: J::deserializer {}; };
      
      





, . . , , , ( ).



もちろん、同じfoo_jsonボむラヌプレヌトを䜜成し、テンプレヌト構造をシリアル化するために䜿甚できる倀フィヌルドのタむプなどのパラメヌタヌを枡すこずは誰も気にしたせん。



蟞曞



連想配列キヌ倀をシリアル化するには、std :: map <>などの蟞曞が必芁です。ほずんどのJSONラむブラリはこのスキヌムに埓っお動䜜したす。オブゞェクトはツリヌにデシリアラむズされ、それを調べお、必芁なフィヌルドを怜玢するなどしたす。そしお、シリアル化のために、動的にそれを埋める必芁がありたす。パフォヌマンスの点では、これは最も効率的な方法ではありたせん。したがっお、そのstd :: map <>をデヌタ構造で䜿甚する前に、それなしで䜕らかの方法で可胜かどうかを考えおください。もちろん、蚭定にJSONを䜿甚する堎合、これは適甚されたせん。

 template<typename T, int R = -1> struct dict { typedef implementation_defined target; typedef implementation_defined serializer; };
      
      





ここで、抂念は配列の抂念ず同じです-Tはキヌず倀のJSON蚘述をパラメヌタヌずしお持぀連想stlコンテナヌです。 䟋

 typedef wjson::dict< std::map< wjson::value<std::string>, wjson::value<int> > > dict_json;
      
      





std :: map <std :: string、int>のシリアル化に䜿甚できたす。蚭蚈は非垞に耇雑ですが、文字列がキヌずしお最も頻繁に䜿甚されるずいう事実を考慮するず、std :: map <std :: string、JSON>にはより簡単なオプションがありたす。

 typedef wjson::dict_map< wjson::value<int> > dict_json;
      
      





もちろん、この蚘事で䞊蚘で説明したJSON゚ンティティは、倀ずしお䜿甚できたす。



パラメヌタヌRは、タむプstd :: vector <std :: pair <>>たたはstd :: deque <std :: pair <>>のペアの順次コンテナヌの予玄のサむズを決定したす。フィヌルド構造は、キヌず倀のペアを蚘述するために䜿甚されたす。

 template<typename K, typename V> struct field;
      
      





ここで、KずVはそれぞれキヌず倀のJSON蚘述です。 䟋

 typedef wjson::dict< std::vector< wjson::field< wjson::value<std::string>, wjson::value<int> > >, 128 /*  */ > dict_json;
      
      





ペアstd :: vector <std :: pair <std :: string、int >>のベクトルをシリアル化する。この蚭蚈は、高いデシリアラむれヌションレヌトが必芁な堎合に䜿甚できたす。ペアのベクトルは、std :: mapよりもはるかに高速で埋められたすもちろん、必芁な予玄が行われおいる堎合。この蚭蚈はさらに耇雑ですが、より頻繁に䜿甚されるため、簡単なオプションがありたす。

 typedef wjson::dict_vector< ::wjson::value<int> > dict_json; typedef wjson::dict_deque< ::wjson::value<int> > dict_json;
      
      







䟋

 int main() { typedef std::vector< std::pair<std::string, int> > dict; typedef wjson::dict_vector< wjson::value<int> > dict_json; dict d; std::string json = "{\"\":1,\"\":2,\"\":3}"; std::cout << json << std::endl; dict_json::serializer()( d, json.begin(), json.end(), 0 ); d.push_back( std::make_pair("",4)); json.clear(); dict_json::serializer()( d, std::back_inserter(json) ); std::cout << json << std::endl; }
      
      





結果

 {"":1,"":2,"":3} {"":1,"":2,"":3,"":4}
      
      





蟞曞は、構成に䜿甚するず䟿利です。最も単玔な堎合、これは単玔なキヌず倀の配列であり、キヌには構成可胜なコンポヌネントの名前が含たれたす。ただし、コンポヌネントの構成を別の構造で取り出しおJSON蚘述を䜜成するのが面倒ではない堎合は、䜙分なランタむムコヌドを削陀し、その結果、初期化゚ラヌの説明を削陀したす。さらに、開発段階で自分ずナヌザヌが喜ぶ最新のフィヌルドセットを䜿甚しお構成を生成するこずができたす。これにより、ナヌザヌは廃止されたずいうプロパティを持぀ドキュメントの関連性を怜蚌できたす。



列挙ずフラグ





䞀般的にデヌタを衚瀺するためのテキスト圢匏が䜜成されたのず同じ理由で、テキスト衚珟で列挙をシリアル化するこずは理にかなっおいたす-これは読みやすいです。長い名前を乱甚しない堎合、少なくずも、数倀ずしおのシリアル化よりも遅くなく、サむズもそれほど高くないこずがわかりたす。これらの行たで読んだ堎合、䞀般的な抂念党䜓が理解できる可胜性が高いので、たずえばすぐに



列挙型シリアル化の䟋


 #include <wjson/json.hpp> #include <wjson/strerror.hpp> #include <iostream> struct counter { typedef enum { one = 1, four = 4, five = 5, two = 2, three = 3, six = 6 } type; }; struct counter_json { JSON_NAME(one) JSON_NAME(two) JSON_NAME(three) JSON_NAME(four) JSON_NAME(five) JSON_NAME2(n_six, " !") typedef wjson::enumerator< counter::type, wjson::member_list< wjson::enum_value< n_one, counter::type, counter::one>, wjson::enum_value< n_two, counter::type, counter::two>, wjson::enum_value< n_three,counter::type, counter::three>, wjson::enum_value< n_four, counter::type, counter::four>, wjson::enum_value< n_five, counter::type, counter::five>, wjson::enum_value< n_six, counter::type, counter::six> > > type; typedef type::serializer serializer; typedef type::target target; typedef type::member_list member_list; }; int main() { typedef wjson::array< std::vector< counter_json > > array_counter_json; std::vector< counter::type > cl; std::string json = "[\"one\",\"two\",\"three\"]"; std::cout << json << std::endl; array_counter_json::serializer()( cl, json.begin(), json.end(), 0 ); cl.push_back(counter::four); cl.push_back(counter::five); cl.push_back(counter::six); array_counter_json::serializer()(cl, std::ostream_iterator<char>(std::cout) ); std::cout << std::endl; }
      
      







䟋に瀺すように、列挙は1察1でシリアル化する必芁はありたせんが、任意の文字列に含めるこずができたす。 結果

 ["one","two","three"] ["one","two","three","four","five"," !"]
      
      





実際、ここでの列挙は䟿宜䞊のものであり、任意の敎数型を䜿甚できたす。



次のJSONを怜蚎しおください。

 {"code":1,"message":"Invalid JSON."}
      
      





これは、ある皮のJSON-RPCスタむルの゚ラヌメッセヌゞです。明らかに、メッセヌゞメッセヌゞはコヌドに盎接関連しおいたす。したがっお、テキストフィヌルドを含む構造を䜜成しお入力する必芁はありたせん。それで十分です。



列挙型ではなく2぀


 #include <wjson/json.hpp> #include <wjson/strerror.hpp> #include <iostream> enum class error_code { ValidJSON = 0, InvalidJSON = 1, ParseError = 2 }; struct error { int code = 0; }; struct code_json { JSON_NAME2(ValidJSON, "Valid JSON.") JSON_NAME2(InvalidJSON, "Invalid JSON.") JSON_NAME2(ParseError, "Parse Error.") typedef wjson::enumerator< int, wjson::member_list< wjson::enum_value< ValidJSON, int, static_cast<int>(error_code::ValidJSON)>, wjson::enum_value< InvalidJSON, int, static_cast<int>(error_code::InvalidJSON)>, wjson::enum_value< ParseError, int, static_cast<int>(error_code::ParseError)> > > type; typedef type::serializer serializer; typedef type::target target; typedef type::member_list member_list; }; struct error_json { JSON_NAME(code) JSON_NAME(message) typedef wjson::object< error, wjson::member_list< wjson::member< n_code, error, int, &error::code>, wjson::member< n_message, error, int, &error::code, code_json> > > type; typedef type::serializer serializer; typedef type::target target; typedef type::member_list member_list; }; int main() { error e; e.code = static_cast<int>(error_code::InvalidJSON); error_json::serializer()(e, std::ostream_iterator<char>(std::cout) ); std::cout << std::endl; }
      
      







結果

 {"code":1,"message":"Invalid JSON."}
      
      





シリアル化はおかしく芋えたすが、逆シリアル化はどうですか「コヌド」は2回デシリアラむズされたす。1回は「コヌド」フィヌルドから、2回目は「メッセヌゞ」フィヌルドからです。error_jsonの個別のバヌゞョンは、「メッセヌゞ」フィヌルドなしで逆シリアル化専甚に䜜成できたすが、シリアル化䞭に解析されるため、パフォヌマンスに倧きな圱響はありたせん。たた、この機胜を䜿甚しお、コヌドの厳密な敎合性を再確認できたす。たずえば、メッセヌゞ内のポむントを疑問笊に眮き換えた堎合

  e = error(); std::string json = "{\"code\":1,\"message\":\"Invalid JSON?\"}"; wjson::json_error ec; error_json::serializer()(e, json.begin(), json.end(), &ec );
      
      





次に、゚ラヌが衚瀺されたす。

 Invalid Enum: {"code":1,"message":">>>Invalid JSON?"}
      
      





列挙は、あらゆる皮類のフラグの組み合わせによく䜿甚されたすが、シリアル化するこずもできたす。しかし、どのような圢で2぀のメ゜ッドが提䟛されたす配列ずしお、たたは指定された区切り文字列ずしお。区切り文字は、最埌のフラグパラメヌタヌによっお指定されたす。コンマ以倖の文字は文字列にシリアル化され、コンマは配列にシリアル化されたす。以䞋のナヌモラスな䟋では、䞡方のオプションが䜿甚されおいたす



祖母は灰色のダギず䜏んでいた


 #include <wjson/json.hpp> #include <wjson/strerror.hpp> #include <iostream> template<char S> struct flags_json { JSON_NAME2(w1, "") JSON_NAME2(w2, "") JSON_NAME2(w4, "") JSON_NAME2(w8, "") JSON_NAME2(w16, "") JSON_NAME2(w32, "") typedef ::wjson::flags< int, wjson::member_list< wjson::enum_value< w1, int, 1>, wjson::enum_value< w2, int, 2>, wjson::enum_value< w4, int, 4>, wjson::enum_value< w8, int, 8>, wjson::enum_value< w16, int, 16>, wjson::enum_value< w32, int, 32> >, S > type; typedef typename type::serializer serializer; typedef typename type::target target; typedef typename type::member_list member_list; }; int main() { std::string json = "\"   \""; int val = 0; flags_json<' '>::serializer()(val, json.begin(), json.end(), 0 ); std::cout << json << " = " << val << std::endl; std::cout << 63 << " = "; flags_json<' '>::serializer()(63, std::ostream_iterator<char>(std::cout) ); std::cout << std::endl; std::cout << 48 << " = "; flags_json<','>::serializer()(48, std::ostream_iterator<char>(std::cout) ); std::cout << std::endl; std::cout << 49 << " = "; flags_json<'|'>::serializer()(49, std::ostream_iterator<char>(std::cout) ); std::cout << std::endl; }
      
      







ここでの考え方は簡単です。私たちは、子䟛の歌の行の各単語を、察応する意味を持぀フラグずしお䜿甚したす。フラグを組み合わせるず、さたざたなオプションが埗られたす。たた、スペヌスをセパレヌタずしお䜿甚する堎合、これがフラグのセットであるこずはたったく明らかではありたせん。最初に、「灰色のダギがありたした」ずいう行をデシリアラむズしたす。これは、1 | 2 | 16 | 32 = 51の組み合わせに察応し、スペヌスずしおセパレヌタヌを䜿甚したす。以䞋は、さたざたな区切り文字を䜿甚したシリアル化の䟋です。明らかに、63たでのすべおの数字を䜿甚できたす-これはフレヌズ党䜓です。

結果

 "   " = 51 63 = "     " 48 = ["",""] 49 = "||"
      
      







おわりに



この創造の発展の歎史に぀いおの長くお退屈な無眪の物語を始めないように、誘惑に負けないこずは非垞に難しいです。したがっお、簡単に。それが構築されおいるfaslibの抂念のいく぀かを解決するために、2008幎にひざたずいお曞かれたした。 2009幎、wjson圓時はコピヌされたコヌドのコレクションでしたが実隓プロゞェクトで䜿甚されたした。それから、むンタヌフェヌスが柔軟ではなく、䞀般的にひどいこずが明らかになりたした。 2011幎には、グロヌバルで包括的か぀正しいこずを行う詊みがありたした。そしお、それはほずんど起こりたしたが、攟棄されたした。同じ幎に、私たちはすべおのプロゞェクトをJSONに倉換し始めたした。珟圚の機胜はすべおのニヌズに察応しおおり、むンタヌフェむスは初心者でも簡単で理解しやすいこずがわかりたした。 2013幎以降、非垞に負荷の高いプロゞェクトを含むすべおのプロゞェクトがwjsonず連携しおいたす。䟋えばcometデヌモンは最倧100䞇の同時アクティブ接続をサポヌトでき、統蚈収集システムは1ホストで1.5 GBを超えるJSON-RPC通知をグラむンドし、1秒あたり最倧450䞇のさたざたなメトリックの倀を登録したす。



私たちはJSONを構成、あらゆる皮類のダンプ、そしおもちろん同じ原則で動䜜するJSON-RPC゚ンゞンず䞀緒に䜿甚したす。これに぀いおはすぐに次の蚘事で説明したす。



wjsonずfaslibは、wjsonが䟝存しおおり、ヘッダヌのみのラむブラリです。サンプルずテストをコンパむルするには

 git clone https://github.com/migashko/faslib.git git clone https://github.com/mambaru/wjson.git #      wjson cd faslib mkdir build cd build cmake .. #     cd ../../wjson mkdir build cd build cmake -DWJSON_BUILD_ALL=ON .. make cd ./tests ctest
      
      





githubのwjsongithub.com/mambaru/wjson



All Articles