diff options
author | bbiff <bbiff@yandex-team.com> | 2022-09-11 00:15:54 +0300 |
---|---|---|
committer | bbiff <bbiff@yandex-team.com> | 2022-09-11 00:15:54 +0300 |
commit | 67576a8962c2506b8ee10da48dffba311acb0042 (patch) | |
tree | 51a695bbddbb9be4e5a0cc5c7ec558ca233c2be5 /library/cpp | |
parent | a8945929eb162f6910b228697c3d2db3b7cb2ca9 (diff) | |
download | ydb-67576a8962c2506b8ee10da48dffba311acb0042.tar.gz |
support duration and timestamp as string in json2proto
Diffstat (limited to 'library/cpp')
-rw-r--r-- | library/cpp/protobuf/json/config.h | 8 | ||||
-rw-r--r-- | library/cpp/protobuf/json/json2proto.cpp | 71 | ||||
-rw-r--r-- | library/cpp/protobuf/json/json2proto.h | 8 | ||||
-rw-r--r-- | library/cpp/protobuf/json/proto2json_printer.cpp | 23 | ||||
-rw-r--r-- | library/cpp/protobuf/json/ut/json2proto_ut.cpp | 26 | ||||
-rw-r--r-- | library/cpp/protobuf/json/ut/proto2json_ut.cpp | 18 | ||||
-rw-r--r-- | library/cpp/protobuf/json/ut/test.proto | 11 |
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, ×tampFromString)) { + 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; } |