diff --git a/3party/jansson/myjansson.hpp b/3party/jansson/myjansson.hpp index d61c3a3357..5a6c438d34 100644 --- a/3party/jansson/myjansson.hpp +++ b/3party/jansson/myjansson.hpp @@ -37,10 +37,11 @@ public: DECLARE_EXCEPTION(Exception, RootException); Json() = default; - explicit Json(std::string const & s) { Attach(s.c_str()); } - explicit Json(char const * s) { Attach(s); } + explicit Json(std::string const & s) { ParseFrom(s); } + explicit Json(char const * s) { ParseFrom(s); } - void Attach(char const * s) + void ParseFrom(std::string const & s) { ParseFrom(s.c_str()); } + void ParseFrom(char const * s) { json_error_t jsonError; m_handle.AttachNew(json_loads(s, 0, &jsonError)); diff --git a/ugc/serdes_json.hpp b/ugc/serdes_json.hpp index 43151efd14..4a88860e4a 100644 --- a/ugc/serdes_json.hpp +++ b/ugc/serdes_json.hpp @@ -1,226 +1,180 @@ #pragma once -#include "ugc/serdes.hpp" #include "ugc/types.hpp" -#include "coding/multilang_utf8_string.hpp" -#include "coding/point_to_integer.hpp" -#include "coding/reader.hpp" -#include "coding/varint.hpp" -#include "coding/write_to_sink.hpp" - #include "base/exception.hpp" #include "3party/jansson/myjansson.hpp" -#include #include -#include +#include namespace ugc { - template - class SerializerJson +template +class SerializerJson +{ +public: + SerializerJson(Sink & sink) : m_sink(sink) {} + ~SerializerJson() { - public: - SerializerJson(Sink & sink, HeaderV0 const & header) : m_sink(sink), m_header(header) {} + char * str = json_dumps(m_json.get(), 0); + std::string json(str); + std::free(static_cast(str)); + m_sink.Write(json.data(), json.size()); + } + void operator()(bool const d, char const * name = nullptr) { ToJSONObject(*m_json, name, d); } + void operator()(uint8_t const d, char const * name = nullptr) { ToJSONObject(*m_json, name, d); } + void operator()(uint32_t const d, char const * name = nullptr) { ToJSONObject(*m_json, name, d); } + void operator()(uint64_t const d, char const * name = nullptr) { ToJSONObject(*m_json, name, d); } + void operator()(std::string const & s, char const * name = nullptr) + { + ToJSONObject(*m_json, name, s); + } - void operator()(bool const d, char const * name = nullptr) + void operator()(Time const & t, char const * name = nullptr) + { + (*this)(ToDaysSinceEpoch(t), name); + } + + void operator()(Sentiment sentiment, char const * name = nullptr) + { + switch (sentiment) { - ToJSONObject(*m_json, name, d); + case Sentiment::Negative: return (*this)(false, name); + case Sentiment::Positive: return (*this)(true, name); } + } - void operator()(uint8_t const d, char const * name = nullptr) - { - ToJSONObject(*m_json, name, d); - } - - void operator()(uint32_t const d, char const * name = nullptr) - { - ToJSONObject(*m_json, name, d); - } - - void operator()(uint64_t const d, char const * name = nullptr) - { - ToJSONObject(*m_json, name, d); - } - - void operator()(std::string const & s, char const * name = nullptr) - { - ToJSONObject(*m_json, name, s); - } - - void operator()(Time const & t, char const * name = nullptr) - { - (*this)(ToDaysSinceEpoch(t), name); - } - - void operator()(Sentiment sentiment, char const * name = nullptr) - { - switch (sentiment) - { - case Sentiment::Negative: return (*this)(false, name); - case Sentiment::Positive: return (*this)(true, name); - } - } - - template - void operator()(vector const & vs, char const * name = nullptr) - { - my::JSONPtr safe_json = std::move(m_json); - m_json = my::NewJSONArray(); + template + void operator()(vector const & vs, char const * name = nullptr) + { + NewScopeWith(my::NewJSONArray(), name, [this, &vs] { for (auto const & v : vs) (*this)(v); - Update(std::move(safe_json), name); - } + }); + } - template - void operator()(R const & r, char const * name = nullptr) - { - my::JSONPtr safe_json = std::move(m_json); - m_json = my::NewJSONObject(); - r.Visit(*this); - Update(std::move(safe_json), name); - } - - void VisitRating(float const f, char const * name = nullptr) - { - CHECK_GREATER_OR_EQUAL(f, 0.0, ()); - auto const d = static_cast(f); - ToJSONObject(*m_json, name, d); - } - - void Flush() - { - std::string json(json_dumps(m_json.get(), 0)); - m_sink.Write(json.data(), json.size()); - } - - private: - - void Update(my::JSONPtr safe_json, char const * name = nullptr) - { - if (safe_json && json_is_array(safe_json)) - json_array_append_new(safe_json.get(), m_json.release()); - else if (safe_json && json_is_object(safe_json)) - json_object_set_new(safe_json.get(), name, m_json.release()); - - if (safe_json) - m_json = std::move(safe_json); - } - - my::JSONPtr m_json = nullptr; - Sink & m_sink; - HeaderV0 const m_header; - }; - - template - class DeserializerJsonV0 + template + void operator()(R const & r, char const * name = nullptr) { - public: - DECLARE_EXCEPTION(Exception, RootException); + NewScopeWith(my::NewJSONObject(), name, [this, &r] { r.Visit(*this); }); + } - DeserializerJsonV0(Source & source, HeaderV0 const & header) : m_source(source), m_header(header) + void VisitRating(float const f, char const * name = nullptr) + { + CHECK_GREATER_OR_EQUAL(f, 0.0, ()); + auto const d = static_cast(f); + ToJSONObject(*m_json, name, d); + } + +private: + template + void NewScopeWith(my::JSONPtr json_object, char const * name, Fn && fn) + { + my::JSONPtr safe_json = std::move(m_json); + m_json = std::move(json_object); + + fn(); + + if (safe_json && json_is_array(safe_json)) + json_array_append_new(safe_json.get(), m_json.release()); + else if (safe_json && json_is_object(safe_json)) + json_object_set_new(safe_json.get(), name, m_json.release()); + + if (safe_json) + m_json = std::move(safe_json); + } + + my::JSONPtr m_json = nullptr; + Sink & m_sink; +}; + +template +class DeserializerJsonV0 +{ +public: + DECLARE_EXCEPTION(Exception, RootException); + + DeserializerJsonV0(Source & source) : m_source(source) + { + std::string src(source.Size(), '\0'); + source.Read(static_cast(&src[0]), source.Size()); + m_jsonObject.ParseFrom(src.c_str()); + m_json = m_jsonObject.get(); + } + + void operator()(bool & d, char const * name = nullptr) { FromJSONObject(m_json, name, d); } + void operator()(uint8_t & d, char const * name = nullptr) { FromJSONObject(m_json, name, d); } + void operator()(uint32_t & d, char const * name = nullptr) { FromJSONObject(m_json, name, d); } + void operator()(uint64_t & d, char const * name = nullptr) { FromJSONObject(m_json, name, d); } + void operator()(std::string & s, char const * name = nullptr) { FromJSONObject(m_json, name, s); } + void operator()(Time & t, char const * name = nullptr) + { + uint32_t d = 0; + FromJSONObject(m_json, name, d); + t = FromDaysSinceEpoch(d); + } + + void operator()(Sentiment & sentiment, char const * name = nullptr) + { + bool s = false; + FromJSONObject(m_json, name, s); + sentiment = s ? Sentiment::Positive : Sentiment::Negative; + } + + template + void operator()(vector & vs, char const * name = nullptr) + { + json_t * context = SaveContext(name); + + if (!json_is_array(m_json)) + MYTHROW(my::Json::Exception, ("The field", name, "must contain a json array.")); + + vs.resize(json_array_size(m_json)); + for (size_t index = 0; index < vs.size(); ++index) { - std::string src; - src.resize(source.Size()); - source.Read(static_cast(&src[0]), source.Size()); - m_jsonObject.Attach(src.c_str()); - m_json = m_jsonObject.get(); - } - - void operator()(bool & d, char const * name = nullptr) - { - FromJSONObject(m_json, name, d); - } - - void operator()(uint8_t & d, char const * name = nullptr) - { - FromJSONObject(m_json, name, d); - } - - void operator()(uint32_t & d, char const * name = nullptr) - { - FromJSONObject(m_json, name, d); - } - - void operator()(uint64_t & d, char const * name = nullptr) - { - FromJSONObject(m_json, name, d); - } - - void operator()(std::string & s, char const * name = nullptr) - { - FromJSONObject(m_json, name, s); - } - - void operator()(Time & t, char const * name = nullptr) - { - uint32_t d = 0; - FromJSONObject(m_json, name, d); - t = FromDaysSinceEpoch(d); - } - - void operator()(Sentiment & sentiment, char const * name = nullptr) - { - bool s = false; - FromJSONObject(m_json, name, s); - sentiment = s ? Sentiment::Positive : Sentiment::Negative; - } - - template - void operator()(vector & vs, char const * name = nullptr) - { - json_t * context = SaveContext(name); - - if (!json_is_array(m_json)) - MYTHROW(my::Json::Exception, ("The field", name, "must contain a json array.")); - - vs.resize(json_array_size(m_json)); - for (size_t index = 0; index < vs.size(); ++index) - { - json_t * context = SaveContext(); - m_json = json_array_get(context, index); - (*this)(vs[index]); - RestoreContext(context); - } - + json_t * context = SaveContext(); + m_json = json_array_get(context, index); + (*this)(vs[index]); RestoreContext(context); } - template - void operator()(R & r, char const * name = nullptr) - { - json_t * context = SaveContext(name); - r.Visit(*this); - RestoreContext(context); - } + RestoreContext(context); + } - void VisitRating(float & f, char const * name = nullptr) - { - double d = 0.0; - FromJSONObject(m_json, name, d); - f = static_cast(d); - } + template + void operator()(R & r, char const * name = nullptr) + { + json_t * context = SaveContext(name); + r.Visit(*this); + RestoreContext(context); + } - private: - json_t * SaveContext(char const * name = nullptr) - { - json_t * context = m_json; - if (name) - m_json = my::GetJSONObligatoryField(context, name); - return context; - } + void VisitRating(float & f, char const * name = nullptr) + { + double d = 0.0; + FromJSONObject(m_json, name, d); + f = static_cast(d); + } - void RestoreContext(json_t * context) - { - if (context) - m_json = context; - } +private: + json_t * SaveContext(char const * name = nullptr) + { + json_t * context = m_json; + if (name) + m_json = my::GetJSONObligatoryField(context, name); + return context; + } - my::Json m_jsonObject; - json_t * m_json = nullptr; - Source & m_source; - HeaderV0 const m_header; - }; + void RestoreContext(json_t * context) + { + if (context) + m_json = context; + } + + my::Json m_jsonObject; + json_t * m_json = nullptr; + Source & m_source; +}; } // namespace ugc diff --git a/ugc/ugc_tests/CMakeLists.txt b/ugc/ugc_tests/CMakeLists.txt index 63638a70a8..333d762dac 100644 --- a/ugc/ugc_tests/CMakeLists.txt +++ b/ugc/ugc_tests/CMakeLists.txt @@ -3,19 +3,18 @@ project(ugc_tests) set( SRC serdes_tests.cpp - serdes_json_tests.cpp ) omim_add_test(${PROJECT_NAME} ${SRC}) omim_link_libraries( ${PROJECT_NAME} ugc - jansson indexer platform coding geometry base + jansson stats_client ${LIBZ} ) diff --git a/ugc/ugc_tests/serdes_json_test.cpp b/ugc/ugc_tests/serdes_json_test.cpp deleted file mode 100644 index d4d16a4780..0000000000 --- a/ugc/ugc_tests/serdes_json_test.cpp +++ /dev/null @@ -1,143 +0,0 @@ -#include "testing/testing.hpp" - -#include "ugc/api.hpp" -#include "ugc/serdes_json.hpp" -#include "ugc/types.hpp" - -#include "coding/reader.hpp" -#include "coding/writer.hpp" - -#include -#include - -using namespace std; -using namespace ugc; - -namespace -{ - using Buffer = vector; - using Ser = SerializerJson>; - using Des = DeserializerJsonV0>; - - Rating GetTestRating() - { - vector records; - records.emplace_back("music" /* key */, 5.0 /* value */); - records.emplace_back("service" /* key */, 4.0 /* value */); - - return Rating(records, 4.5 /* aggValue */); - } - - MemWriter MakeSink(Buffer & buffer) { return MemWriter(buffer); } - - ReaderSource MakeSource(Buffer const & buffer) - { - MemReader reader(buffer.data(), buffer.size()); - return ReaderSource(reader); - } - - UNIT_TEST(SerDes_Json_Rating) - { - auto expectedRating = GetTestRating(); - TEST_EQUAL(expectedRating, expectedRating, ()); - - HeaderV0 header; - - Buffer buffer; - - { - auto sink = MakeSink(buffer); - Ser ser(sink, header); - ser(expectedRating); - ser.Flush(); - } - - Rating actualRating({} /* ratings */, {} /* aggValue */); - - { - auto source = MakeSource(buffer); - Des des(source, header); - des(actualRating); - } - - TEST_EQUAL(expectedRating, actualRating, ()); - } - - UNIT_TEST(SerDes_Json_Reviews) - { - auto expectedUGC = Api::MakeTestUGC1().m_reviews; - TEST_EQUAL(expectedUGC, expectedUGC, ()); - - HeaderV0 header; - - Buffer buffer; - - { - auto sink = MakeSink(buffer); - Ser ser(sink, header); - ser(expectedUGC); - ser.Flush(); - } - - std::vector actualUGC({} /* rating */, {} /* reviews */, {} /* attributes */); - { - auto source = MakeSource(buffer); - Des des(source, header); - des(actualUGC); - } - - TEST_EQUAL(expectedUGC, actualUGC, ()); - } - - UNIT_TEST(SerDes_Json_Attributes) - { - auto expectedUGC = Api::MakeTestUGC1().m_attributes; - TEST_EQUAL(expectedUGC, expectedUGC, ()); - - HeaderV0 header; - - Buffer buffer; - - { - auto sink = MakeSink(buffer); - Ser ser(sink, header); - ser(expectedUGC); - ser.Flush(); - } - - std::vector actualUGC({} /* rating */, {} /* reviews */, {} /* attributes */); - { - auto source = MakeSource(buffer); - Des des(source, header); - des(actualUGC); - } - - TEST_EQUAL(expectedUGC, actualUGC, ()); - } - - UNIT_TEST(SerDes_Json_UGC) - { - auto expectedUGC = Api::MakeTestUGC1(); - TEST_EQUAL(expectedUGC, expectedUGC, ()); - - HeaderV0 header; - - Buffer buffer; - - { - auto sink = MakeSink(buffer); - Ser ser(sink, header); - ser(expectedUGC); - ser.Flush(); - } - - UGC actualUGC({} /* rating */, {} /* reviews */, {} /* attributes */); - { - auto source = MakeSource(buffer); - Des des(source, header); - des(actualUGC); - } - - TEST_EQUAL(expectedUGC, actualUGC, ()); - } -} // namespace diff --git a/ugc/ugc_tests/serdes_tests.cpp b/ugc/ugc_tests/serdes_tests.cpp index 79465ad522..8271ec8e98 100644 --- a/ugc/ugc_tests/serdes_tests.cpp +++ b/ugc/ugc_tests/serdes_tests.cpp @@ -2,6 +2,7 @@ #include "ugc/api.hpp" #include "ugc/serdes.hpp" +#include "ugc/serdes_json.hpp" #include "ugc/types.hpp" #include "coding/reader.hpp" @@ -16,8 +17,11 @@ using namespace ugc; namespace { using Buffer = vector; -using Ser = Serializer>; -using Des = DeserializerV0>; +using ToBin = Serializer>; +using FromBin = DeserializerV0>; +using ToJson = SerializerJson>; +using FromJson = DeserializerJsonV0>; + Rating GetTestRating() { @@ -36,28 +40,64 @@ ReaderSource MakeSource(Buffer const & buffer) return ReaderSource(reader); } +template +void MakeTest(Object const & src) +{ + Buffer buffer; + Object trg; + + { + auto sink = MakeSink(buffer); + Serializator ser(sink); + ser(src); + } + + { + auto source = MakeSource(buffer); + Deserializator des(source); + des(trg); + } + TEST_EQUAL(src, trg, ()); +} + UNIT_TEST(SerDes_Rating) { auto const expectedRating = GetTestRating(); TEST_EQUAL(expectedRating, expectedRating, ()); - Buffer buffer; + MakeTest(expectedRating); +} - { - auto sink = MakeSink(buffer); - Ser ser(sink); - ser(expectedRating); - } +UNIT_TEST(SerDes_Json_Rating) +{ + auto const expectedRating = GetTestRating(); + TEST_EQUAL(expectedRating, expectedRating, ()); - Rating actualRating({} /* ratings */, {} /* aggValue */); + MakeTest(expectedRating); +} - { - auto source = MakeSource(buffer); - Des des(source); - des(actualRating); - } +UNIT_TEST(SerDes_Json_Reviews) +{ + auto expectedUGC = Api::MakeTestUGC1(Time(chrono::hours(24 * 100))).m_reviews; + TEST_EQUAL(expectedUGC, expectedUGC, ()); - TEST_EQUAL(expectedRating, actualRating, ()); + MakeTest(expectedUGC); +} + +UNIT_TEST(SerDes_Json_Attributes) +{ + auto expectedUGC = Api::MakeTestUGC1(Time(chrono::hours(24 * 100))).m_attributes; + TEST_EQUAL(expectedUGC, expectedUGC, ()); + + MakeTest(expectedUGC); +} + +UNIT_TEST(SerDes_Json_UGC) +{ + auto expectedUGC = Api::MakeTestUGC1(Time(chrono::hours(24 * 100))); + TEST_EQUAL(expectedUGC, expectedUGC, ()); + + MakeTest(expectedUGC); } UNIT_TEST(SerDes_UGC) diff --git a/ugc/ugc_tests/ugc_tests.pro b/ugc/ugc_tests/ugc_tests.pro index 2537430a24..4cc55a6895 100644 --- a/ugc/ugc_tests/ugc_tests.pro +++ b/ugc/ugc_tests/ugc_tests.pro @@ -6,7 +6,7 @@ CONFIG -= app_bundle TEMPLATE = app ROOT_DIR = ../.. -DEPENDENCIES = ugc jansson indexer platform coding geometry base stats_client +DEPENDENCIES = ugc indexer platform coding geometry base jansson stats_client macx-* { LIBS *= "-framework IOKit" "-framework SystemConfiguration" @@ -19,4 +19,3 @@ QT *= core SOURCES += \ ../../testing/testingmain.cpp \ serdes_tests.cpp \ - serdes_json_tests.cpp \ diff --git a/xcode/ugc/ugc.xcodeproj/project.pbxproj b/xcode/ugc/ugc.xcodeproj/project.pbxproj index 4701854d91..03b7907b95 100644 --- a/xcode/ugc/ugc.xcodeproj/project.pbxproj +++ b/xcode/ugc/ugc.xcodeproj/project.pbxproj @@ -16,7 +16,6 @@ 670E7BA31EF980A600A8E9ED /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 670E7BA21EF980A600A8E9ED /* libz.tbd */; }; 670E7BA51EF980B300A8E9ED /* libcoding.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 670E7BA41EF980B300A8E9ED /* libcoding.a */; }; 670E7BCC1EFA66DD00A8E9ED /* libugc.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F6F8E3A41EF83D7600F2DE8F /* libugc.a */; }; - 670E7BCE1EFA6B4100A8E9ED /* serdes_json_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 670E7BCD1EFA6B4100A8E9ED /* serdes_json_test.cpp */; }; 670E7BD31EFA774800A8E9ED /* libjansson.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 670E7BD21EFA774800A8E9ED /* libjansson.a */; }; F6150E221EF90040000B955D /* api.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F6150E1F1EF90040000B955D /* api.cpp */; }; F6150E5D1EFAAB45000B955D /* storage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F6150E5B1EFAAB45000B955D /* storage.cpp */; }; @@ -45,7 +44,6 @@ 670E7BA21EF980A600A8E9ED /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; 670E7BA41EF980B300A8E9ED /* libcoding.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libcoding.a; path = "../../../../Library/Developer/Xcode/DerivedData/omim-gzleizqujktwggdwiejzkgjrsgvp/Build/Products/Debug/libcoding.a"; sourceTree = ""; }; 670E7BA61EF980E900A8E9ED /* serdes.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = serdes.hpp; sourceTree = ""; }; - 670E7BCD1EFA6B4100A8E9ED /* serdes_json_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serdes_json_test.cpp; sourceTree = ""; }; 670E7BD01EFA6D7400A8E9ED /* serdes_json.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = serdes_json.hpp; sourceTree = ""; }; 670E7BD21EFA774800A8E9ED /* libjansson.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libjansson.a; path = "../../../../Library/Developer/Xcode/DerivedData/omim-gzleizqujktwggdwiejzkgjrsgvp/Build/Products/Debug/libjansson.a"; sourceTree = ""; }; F6150E1F1EF90040000B955D /* api.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = api.cpp; sourceTree = ""; }; @@ -89,7 +87,6 @@ children = ( 670E7B961EF97D0E00A8E9ED /* testingmain.cpp */, 670E7B941EF97CE700A8E9ED /* serdes_tests.cpp */, - 670E7BCD1EFA6B4100A8E9ED /* serdes_json_test.cpp */, ); name = ugc_tests; path = ../../ugc/ugc_tests; @@ -121,7 +118,6 @@ children = ( F6150E5B1EFAAB45000B955D /* storage.cpp */, F6150E5C1EFAAB45000B955D /* storage.hpp */, - 670E7BA61EF980E900A8E9ED /* serdes.hpp */, F6150E1F1EF90040000B955D /* api.cpp */, F6150E201EF90040000B955D /* api.hpp */, 670E7BD01EFA6D7400A8E9ED /* serdes_json.hpp */, @@ -238,7 +234,6 @@ files = ( 670E7B981EF97D1000A8E9ED /* serdes_tests.cpp in Sources */, 670E7B991EF97D1600A8E9ED /* testingmain.cpp in Sources */, - 670E7BCE1EFA6B4100A8E9ED /* serdes_json_test.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };