Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/ugc
diff options
context:
space:
mode:
authorSergey Yershov <syershov@maps.me>2017-06-23 22:32:15 +0300
committerYuri Gorshenin <mipt.vi002@gmail.com>2017-07-05 16:41:38 +0300
commit8a5fcdf2cf8eec8812f731a70997e59be009f776 (patch)
tree5db010e08dccf7ad4769d4f3df81534098e9f643 /ugc
parent4f0146194b6e4246ab8976a8545a419c587ba6df (diff)
Serialization and deserealization UGC structures to/from JSON
Diffstat (limited to 'ugc')
-rw-r--r--ugc/serdes_json.hpp226
-rw-r--r--ugc/ugc_tests/CMakeLists.txt3
-rw-r--r--ugc/ugc_tests/serdes_json_test.cpp91
-rw-r--r--ugc/ugc_tests/ugc_tests.pro3
4 files changed, 321 insertions, 2 deletions
diff --git a/ugc/serdes_json.hpp b/ugc/serdes_json.hpp
new file mode 100644
index 0000000000..43151efd14
--- /dev/null
+++ b/ugc/serdes_json.hpp
@@ -0,0 +1,226 @@
+#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 <cmath>
+#include <cstdint>
+#include <iostream>
+
+namespace ugc
+{
+ template <typename Sink>
+ class SerializerJson
+ {
+ public:
+ SerializerJson(Sink & sink, HeaderV0 const & header) : m_sink(sink), m_header(header) {}
+
+ 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()(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 <typename T>
+ void operator()(vector<T> const & vs, char const * name = nullptr)
+ {
+ my::JSONPtr safe_json = std::move(m_json);
+ m_json = my::NewJSONArray();
+ for (auto const & v : vs)
+ (*this)(v);
+ Update(std::move(safe_json), name);
+ }
+
+ template <typename R>
+ 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<double>(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 <typename Source>
+ class DeserializerJsonV0
+ {
+ public:
+ DECLARE_EXCEPTION(Exception, RootException);
+
+ DeserializerJsonV0(Source & source, HeaderV0 const & header) : m_source(source), m_header(header)
+ {
+ std::string src;
+ src.resize(source.Size());
+ source.Read(static_cast<void *>(&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 <typename T>
+ void operator()(vector<T> & 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);
+ }
+
+ RestoreContext(context);
+ }
+
+ template <typename R>
+ void operator()(R & r, char const * name = nullptr)
+ {
+ json_t * context = SaveContext(name);
+ r.Visit(*this);
+ RestoreContext(context);
+ }
+
+ void VisitRating(float & f, char const * name = nullptr)
+ {
+ double d = 0.0;
+ FromJSONObject(m_json, name, d);
+ f = static_cast<float>(d);
+ }
+
+ private:
+ json_t * SaveContext(char const * name = nullptr)
+ {
+ json_t * context = m_json;
+ if (name)
+ m_json = my::GetJSONObligatoryField(context, name);
+ return context;
+ }
+
+ void RestoreContext(json_t * context)
+ {
+ if (context)
+ m_json = context;
+ }
+
+ my::Json m_jsonObject;
+ json_t * m_json = nullptr;
+ Source & m_source;
+ HeaderV0 const m_header;
+ };
+} // namespace ugc
diff --git a/ugc/ugc_tests/CMakeLists.txt b/ugc/ugc_tests/CMakeLists.txt
index b8501676c0..63638a70a8 100644
--- a/ugc/ugc_tests/CMakeLists.txt
+++ b/ugc/ugc_tests/CMakeLists.txt
@@ -3,12 +3,14 @@ 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
@@ -17,5 +19,4 @@ omim_link_libraries(
stats_client
${LIBZ}
)
-
link_qt5_core(${PROJECT_NAME})
diff --git a/ugc/ugc_tests/serdes_json_test.cpp b/ugc/ugc_tests/serdes_json_test.cpp
new file mode 100644
index 0000000000..6f7179da8c
--- /dev/null
+++ b/ugc/ugc_tests/serdes_json_test.cpp
@@ -0,0 +1,91 @@
+#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 <cstdint>
+#include <vector>
+
+using namespace std;
+using namespace ugc;
+
+namespace
+{
+ using Buffer = vector<uint8_t>;
+ using Ser = SerializerJson<MemWriter<Buffer>>;
+ using Des = DeserializerJsonV0<ReaderSource<MemReader>>;
+
+ Rating GetTestRating()
+ {
+ vector<RatingRecord> records;
+ records.emplace_back("music" /* key */, 5.0 /* value */);
+ records.emplace_back("service" /* key */, 4.0 /* value */);
+
+ return Rating(records, 4.5 /* aggValue */);
+ }
+
+ MemWriter<Buffer> MakeSink(Buffer & buffer) { return MemWriter<Buffer>(buffer); }
+
+ ReaderSource<MemReader> MakeSource(Buffer const & buffer)
+ {
+ MemReader reader(buffer.data(), buffer.size());
+ return ReaderSource<MemReader>(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_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/ugc_tests.pro b/ugc/ugc_tests/ugc_tests.pro
index bc20f3e7a3..2537430a24 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 indexer platform coding geometry base stats_client
+DEPENDENCIES = ugc jansson indexer platform coding geometry base stats_client
macx-* {
LIBS *= "-framework IOKit" "-framework SystemConfiguration"
@@ -19,3 +19,4 @@ QT *= core
SOURCES += \
../../testing/testingmain.cpp \
serdes_tests.cpp \
+ serdes_json_tests.cpp \