diff options
author | dimastark <dimastark@yandex-team.com> | 2024-09-30 16:58:58 +0300 |
---|---|---|
committer | dimastark <dimastark@yandex-team.com> | 2024-09-30 17:11:11 +0300 |
commit | 4cd5d4661db60c7f096ec51f08bca0ed7aba8f9b (patch) | |
tree | 03a54adef9e505da807b2456aeadfc29a2b1ece6 /library/cpp | |
parent | 334072c7351043236bdca34f2ede8ed1cb4a6d79 (diff) | |
download | ydb-4cd5d4661db60c7f096ec51f08bca0ed7aba8f9b.tar.gz |
SortMapKeys in proto2json for deterministic seriarialization
commit_hash:e64dedae589fa2d16347be4d80e2596f46566b38
Diffstat (limited to 'library/cpp')
-rw-r--r-- | library/cpp/protobuf/json/config.h | 8 | ||||
-rw-r--r-- | library/cpp/protobuf/json/proto2json_printer.cpp | 19 | ||||
-rw-r--r-- | library/cpp/protobuf/json/ut/proto2json_ut.cpp | 18 |
3 files changed, 43 insertions, 2 deletions
diff --git a/library/cpp/protobuf/json/config.h b/library/cpp/protobuf/json/config.h index 98d484cdf4..d0d2d21bcf 100644 --- a/library/cpp/protobuf/json/config.h +++ b/library/cpp/protobuf/json/config.h @@ -109,6 +109,9 @@ namespace NProtobufJson { bool WriteNanAsString = false; + // Sort keys in maps before serialization. + bool SortMapKeys = false; + TSelf& SetDoubleNDigits(ui32 ndigits) { DoubleNDigits = ndigits; return *this; @@ -189,6 +192,11 @@ namespace NProtobufJson { return *this; } + TSelf& SetSortMapKeys(bool value) { + SortMapKeys = value; + return *this; + } + TSelf& SetStringifyNumbers(EStringifyNumbersMode stringify) { StringifyNumbers = stringify; return *this; diff --git a/library/cpp/protobuf/json/proto2json_printer.cpp b/library/cpp/protobuf/json/proto2json_printer.cpp index 5d0e140615..a9f8c3fce9 100644 --- a/library/cpp/protobuf/json/proto2json_printer.cpp +++ b/library/cpp/protobuf/json/proto2json_printer.cpp @@ -8,6 +8,7 @@ #include <library/cpp/protobuf/json/proto/enum_options.pb.h> +#include <util/generic/map.h> #include <util/generic/yexception.h> #include <util/string/ascii.h> #include <util/string/cast.h> @@ -394,8 +395,22 @@ namespace NProtobufJson { case FieldDescriptor::CPPTYPE_MESSAGE: { if (isMap) { - for (size_t i = 0, endI = reflection->FieldSize(proto, &field); i < endI; ++i) { - PrintKeyValue(reflection->GetRepeatedMessage(proto, &field, i), json); + if (GetConfig().SortMapKeys) { + TMap<TString, size_t> keyToIndex; + for (size_t i = 0, endI = reflection->FieldSize(proto, &field); i < endI; ++i) { + const Message& fieldMessage = reflection->GetRepeatedMessage(proto, &field, i); + const FieldDescriptor* keyField = fieldMessage.GetDescriptor()->map_key(); + Y_ABORT_UNLESS(keyField, "Map entry key field not found."); + TString key = MakeKey(fieldMessage, *keyField); + keyToIndex[key] = i; + } + for (const auto& [_, i] : keyToIndex) { + PrintKeyValue(reflection->GetRepeatedMessage(proto, &field, i), json); + } + } else { + for (size_t i = 0, endI = reflection->FieldSize(proto, &field); i < endI; ++i) { + PrintKeyValue(reflection->GetRepeatedMessage(proto, &field, i), json); + } } } else { for (size_t i = 0, endI = reflection->FieldSize(proto, &field); i < endI; ++i) { diff --git a/library/cpp/protobuf/json/ut/proto2json_ut.cpp b/library/cpp/protobuf/json/ut/proto2json_ut.cpp index 9e98ab8a06..f5bcfac49d 100644 --- a/library/cpp/protobuf/json/ut/proto2json_ut.cpp +++ b/library/cpp/protobuf/json/ut/proto2json_ut.cpp @@ -997,6 +997,24 @@ Y_UNIT_TEST(TestMapUsingGeneratedAsJSON) { UNIT_ASSERT_JSON_STRINGS_EQUAL(jsonStr.Str(), modelStr); } // TestMapUsingGeneratedAsJSON +Y_UNIT_TEST(TestMapSortedKeys) { + TMapType proto; + + auto& items = *proto.MutableItems(); + items["key1"] = "value1"; + items["key2"] = "value2"; + items["key3"] = "value3"; + + TString modelStr(R"_({"Items":{"key1":"value1","key2":"value2","key3":"value3"}})_"); + + TStringStream jsonStr; + + auto config = TProto2JsonConfig().SetMapAsObject(true).SetSortMapKeys(false); + UNIT_ASSERT_NO_EXCEPTION(Proto2Json(proto, jsonStr, config)); + + UNIT_ASSERT_JSON_STRINGS_EQUAL(jsonStr.Str(), modelStr); +} // TestMapSortedKeys + Y_UNIT_TEST(TestMapDefaultValue) { TMapType proto; |