aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp
diff options
context:
space:
mode:
authorbbiff <bbiff@yandex-team.com>2022-09-11 00:15:54 +0300
committerbbiff <bbiff@yandex-team.com>2022-09-11 00:15:54 +0300
commit67576a8962c2506b8ee10da48dffba311acb0042 (patch)
tree51a695bbddbb9be4e5a0cc5c7ec558ca233c2be5 /library/cpp
parenta8945929eb162f6910b228697c3d2db3b7cb2ca9 (diff)
downloadydb-67576a8962c2506b8ee10da48dffba311acb0042.tar.gz
support duration and timestamp as string in json2proto
Diffstat (limited to 'library/cpp')
-rw-r--r--library/cpp/protobuf/json/config.h8
-rw-r--r--library/cpp/protobuf/json/json2proto.cpp71
-rw-r--r--library/cpp/protobuf/json/json2proto.h8
-rw-r--r--library/cpp/protobuf/json/proto2json_printer.cpp23
-rw-r--r--library/cpp/protobuf/json/ut/json2proto_ut.cpp26
-rw-r--r--library/cpp/protobuf/json/ut/proto2json_ut.cpp18
-rw-r--r--library/cpp/protobuf/json/ut/test.proto11
7 files changed, 165 insertions, 0 deletions
diff --git a/library/cpp/protobuf/json/config.h b/library/cpp/protobuf/json/config.h
index dc84fb4d5d..8b9d60ab4f 100644
--- a/library/cpp/protobuf/json/config.h
+++ b/library/cpp/protobuf/json/config.h
@@ -65,6 +65,9 @@ namespace NProtobufJson {
/// with FieldNameMode.
bool UseJsonName = false;
+ // Allow nonstandard conversions, e.g. from google.protobuf.Duration to string
+ bool ConvertTimeAsString = false;
+
/// Transforms will be applied only to string values (== protobuf fields of string / bytes type).
/// yajl_encode_string will be used if no transforms are specified.
TVector<TStringTransformPtr> StringTransforms;
@@ -125,6 +128,11 @@ namespace NProtobufJson {
return *this;
}
+ TSelf& SetConvertTimeAsString(bool value) {
+ ConvertTimeAsString = value;
+ return *this;
+ }
+
TSelf& SetExtensionFieldNameMode(ExtFldNameMode mode) {
ExtensionFieldNameMode = mode;
return *this;
diff --git a/library/cpp/protobuf/json/json2proto.cpp b/library/cpp/protobuf/json/json2proto.cpp
index 92bfe9755b..903ffa1a85 100644
--- a/library/cpp/protobuf/json/json2proto.cpp
+++ b/library/cpp/protobuf/json/json2proto.cpp
@@ -3,6 +3,7 @@
#include <library/cpp/json/json_value.h>
+#include <google/protobuf/util/time_util.h>
#include <google/protobuf/message.h>
#include <google/protobuf/descriptor.h>
@@ -77,6 +78,52 @@ static TString GetFieldName(const google::protobuf::FieldDescriptor& field,
return name;
}
+
+static void JsonString2Duration(const NJson::TJsonValue& json,
+ google::protobuf::Message& proto,
+ const google::protobuf::FieldDescriptor& field,
+ const NProtobufJson::TJson2ProtoConfig& config) {
+ using namespace google::protobuf;
+ if (!json.GetString() && !config.CastRobust) {
+ ythrow yexception() << "Invalid type of JSON field '" << field.name() << "': "
+ << "IsString() failed while "
+ << "CPPTYPE_STRING is expected.";
+ }
+ TString jsonString = json.GetStringRobust();
+
+ Duration durationFromString;
+
+ if (!util::TimeUtil::FromString(jsonString, &durationFromString)) {
+ ythrow yexception() << "error while parsing google.protobuf.Duration from string on field '" <<
+ field.name() << "'";
+ }
+
+ proto.CopyFrom(durationFromString);
+
+}
+
+static void JsonString2Timestamp(const NJson::TJsonValue& json,
+ google::protobuf::Message& proto,
+ const google::protobuf::FieldDescriptor& field,
+ const NProtobufJson::TJson2ProtoConfig& config) {
+ using namespace google::protobuf;
+ if (!json.GetString() && !config.CastRobust) {
+ ythrow yexception() << "Invalid type of JSON field '" << field.name() << "': "
+ << "IsString() failed while "
+ << "CPPTYPE_STRING is expected.";
+ }
+ TString jsonString = json.GetStringRobust();
+
+ Timestamp timestampFromString;
+
+ if (!util::TimeUtil::FromString(jsonString, &timestampFromString)) {
+ ythrow yexception() << "error while parsing google.protobuf.Timestamp from string on field '" <<
+ field.name() << "'";
+ }
+
+ proto.CopyFrom(timestampFromString);
+}
+
static void
JsonString2Field(const NJson::TJsonValue& json,
google::protobuf::Message& proto,
@@ -110,6 +157,24 @@ JsonString2Field(const NJson::TJsonValue& json,
reflection->SetString(&proto, &field, value);
}
+static bool
+HandleString2TimeConversion(const NJson::TJsonValue& json,
+ google::protobuf::Message& proto,
+ const google::protobuf::FieldDescriptor& field,
+ const NProtobufJson::TJson2ProtoConfig& config) {
+ using namespace google::protobuf;
+ auto type = proto.GetDescriptor()->well_known_type();
+
+ if (type == Descriptor::WellKnownType::WELLKNOWNTYPE_DURATION) {
+ JsonString2Duration(json, proto, field, config);
+ return true;
+ } else if (type == Descriptor::WellKnownType::WELLKNOWNTYPE_TIMESTAMP) {
+ JsonString2Timestamp(json, proto, field, config);
+ return true;
+ }
+ return false;
+}
+
static const NProtoBuf::EnumValueDescriptor*
FindEnumValue(const NProtoBuf::EnumDescriptor* enumField,
TStringBuf target, bool (*equals)(TStringBuf, TStringBuf)) {
@@ -215,6 +280,12 @@ Json2SingleField(const NJson::TJsonValue& json,
case FieldDescriptor::CPPTYPE_MESSAGE: {
Message* innerProto = reflection->MutableMessage(&proto, &field);
Y_ASSERT(!!innerProto);
+
+ if (config.AllowString2TimeConversion &&
+ HandleString2TimeConversion(fieldJson, *innerProto, field, config)) {
+ break;
+ }
+
NProtobufJson::MergeJson2Proto(fieldJson, *innerProto, config);
break;
diff --git a/library/cpp/protobuf/json/json2proto.h b/library/cpp/protobuf/json/json2proto.h
index 4c33498dfa..ed89c6cf63 100644
--- a/library/cpp/protobuf/json/json2proto.h
+++ b/library/cpp/protobuf/json/json2proto.h
@@ -103,6 +103,11 @@ namespace NProtobufJson {
return *this;
}
+ TSelf& SetAllowString2TimeConversion(bool value) {
+ AllowString2TimeConversion = value;
+ return *this;
+ }
+
FldNameMode FieldNameMode = FieldNameOriginalCase;
bool AllowUnknownFields = true;
@@ -144,6 +149,9 @@ namespace NProtobufJson {
/// Allow js-style comments (both // and /**/)
bool AllowComments = false;
+
+ /// Allow nonstandard conversions, e.g. google.protobuf.Duration from String
+ bool AllowString2TimeConversion = false;
};
/// @throw yexception
diff --git a/library/cpp/protobuf/json/proto2json_printer.cpp b/library/cpp/protobuf/json/proto2json_printer.cpp
index 6123eab0f2..409147dc06 100644
--- a/library/cpp/protobuf/json/proto2json_printer.cpp
+++ b/library/cpp/protobuf/json/proto2json_printer.cpp
@@ -2,9 +2,13 @@
#include "config.h"
#include "util.h"
+#include <google/protobuf/util/time_util.h>
+
#include <util/generic/yexception.h>
#include <util/string/ascii.h>
#include <util/string/cast.h>
+#include <util/system/win_undef.h>
+
namespace NProtobufJson {
using namespace NProtoBuf;
@@ -176,6 +180,22 @@ namespace NProtobufJson {
}
}
+ bool HandleTimeConversion(const Message& proto, IJsonOutput& json) {
+ using namespace google::protobuf;
+ auto type = proto.GetDescriptor()->well_known_type();
+
+ if (type == Descriptor::WellKnownType::WELLKNOWNTYPE_DURATION) {
+ const auto& duration = static_cast<const Duration&>(proto);
+ json.Write(util::TimeUtil::ToString(duration));
+ return true;
+ } else if (type == Descriptor::WellKnownType::WELLKNOWNTYPE_TIMESTAMP) {
+ const auto& timestamp = static_cast<const Timestamp&>(proto);
+ json.Write(util::TimeUtil::ToString(timestamp));
+ return true;
+ }
+ return false;
+ }
+
void TProto2JsonPrinter::PrintSingleField(const Message& proto,
const FieldDescriptor& field,
IJsonOutput& json,
@@ -228,6 +248,9 @@ namespace NProtobufJson {
case FieldDescriptor::CPPTYPE_MESSAGE: {
json.WriteKey(key);
+ if (Config.ConvertTimeAsString && HandleTimeConversion(reflection->GetMessage(proto, &field), json)) {
+ break;
+ }
Print(reflection->GetMessage(proto, &field), json);
break;
}
diff --git a/library/cpp/protobuf/json/ut/json2proto_ut.cpp b/library/cpp/protobuf/json/ut/json2proto_ut.cpp
index 0dfe57bc7a..d213c9e913 100644
--- a/library/cpp/protobuf/json/ut/json2proto_ut.cpp
+++ b/library/cpp/protobuf/json/ut/json2proto_ut.cpp
@@ -8,6 +8,7 @@
#include <library/cpp/json/json_reader.h>
#include <library/cpp/json/json_writer.h>
+#include <library/cpp/protobuf/interop/cast.h>
#include <library/cpp/protobuf/json/json2proto.h>
#include <library/cpp/testing/unittest/registar.h>
@@ -1144,4 +1145,29 @@ Y_UNIT_TEST(TestAllowComments) {
UNIT_ASSERT_VALUES_EQUAL(proto.GetI64(), 3423);
} // TestAllowComments
+Y_UNIT_TEST(TestSimplifiedDuration) {
+ NJson::TJsonValue json;
+ TSingleDuration simpleDuration;
+ json["Duration"] = "10.1s";
+ NProtobufJson::Json2Proto(json, simpleDuration, NProtobufJson::TJson2ProtoConfig().SetAllowString2TimeConversion(true));
+ UNIT_ASSERT_EQUAL(NProtoInterop::CastFromProto(simpleDuration.GetDuration()), TDuration::MilliSeconds(10100));
+} // TestSimplifiedDuration
+
+Y_UNIT_TEST(TestUnwrappedDuration) {
+ NJson::TJsonValue json;
+ TSingleDuration duration;
+ json["Duration"]["seconds"] = 2;
+ NProtobufJson::Json2Proto(json, duration, NProtobufJson::TJson2ProtoConfig());
+ UNIT_ASSERT_EQUAL(NProtoInterop::CastFromProto(duration.GetDuration()), TDuration::MilliSeconds(2000));
+} // TestUnwrappedDuration
+
+Y_UNIT_TEST(TestSimplifiedTimestamp) {
+ NJson::TJsonValue json;
+ TSingleTimestamp simpleTimestamp;
+ json["Timestamp"] = "2014-08-26T15:52:15Z";
+ NProtobufJson::Json2Proto(json, simpleTimestamp, NProtobufJson::TJson2ProtoConfig().SetAllowString2TimeConversion(true));
+ UNIT_ASSERT_EQUAL(NProtoInterop::CastFromProto(simpleTimestamp.GetTimestamp()), TInstant::ParseIso8601("2014-08-26T15:52:15Z"));
+
+} // TestSimplifiedTimestamp
+
} // TJson2ProtoTest
diff --git a/library/cpp/protobuf/json/ut/proto2json_ut.cpp b/library/cpp/protobuf/json/ut/proto2json_ut.cpp
index 07e52d7f2f..d4d6d374e9 100644
--- a/library/cpp/protobuf/json/ut/proto2json_ut.cpp
+++ b/library/cpp/protobuf/json/ut/proto2json_ut.cpp
@@ -1019,4 +1019,22 @@ Y_UNIT_TEST(TestExtension) {
UNIT_ASSERT_EQUAL(Proto2Json(proto, cfg), "{\"bar\":1}");
} // TestExtension
+Y_UNIT_TEST(TestSimplifiedDuration) {
+ TString json;
+ TSingleDuration simpleDuration;
+ simpleDuration.mutable_duration()->set_seconds(10);
+ simpleDuration.mutable_duration()->set_nanos(101);
+ Proto2Json(simpleDuration, json, TProto2JsonConfig().SetConvertTimeAsString(true));
+ UNIT_ASSERT_EQUAL_C(json, "{\"Duration\":\"10.000000101s\"}", "real value is " << json);
+} // TestSimplifiedDuration
+
+Y_UNIT_TEST(TestSimplifiedTimestamp) {
+ TString json;
+ TSingleTimestamp simpleTimestamp;
+ simpleTimestamp.mutable_timestamp()->set_seconds(10000000);
+ simpleTimestamp.mutable_timestamp()->set_nanos(504);
+ Proto2Json(simpleTimestamp, json, TProto2JsonConfig().SetConvertTimeAsString(true));
+ UNIT_ASSERT_EQUAL_C(json, "{\"Timestamp\":\"1970-04-26T17:46:40.000000504Z\"}", "real value is " << json);
+} // TestSimplifiedTimestamp
+
} // TProto2JsonTest
diff --git a/library/cpp/protobuf/json/ut/test.proto b/library/cpp/protobuf/json/ut/test.proto
index 0fa996fd41..6bc1984373 100644
--- a/library/cpp/protobuf/json/ut/test.proto
+++ b/library/cpp/protobuf/json/ut/test.proto
@@ -1,5 +1,8 @@
package NProtobufJsonTest;
+import "google/protobuf/duration.proto";
+import "google/protobuf/timestamp.proto";
+
enum EEnum {
E_0 = 0;
E_1 = 1;
@@ -194,6 +197,14 @@ message TSingleRepeatedInt {
repeated int32 RepeatedInt = 1;
}
+message TSingleDuration {
+ required google.protobuf.Duration Duration = 1;
+}
+
+message TSingleTimestamp {
+ required google.protobuf.Timestamp Timestamp = 1;
+}
+
message TExtensionField {
extensions 100 to 199;
}