proto2json_printer.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. #include "proto2json_printer.h"
  2. #include "config.h"
  3. #include "util.h"
  4. #include <google/protobuf/any.pb.h>
  5. #include <google/protobuf/dynamic_message.h>
  6. #include <google/protobuf/util/time_util.h>
  7. #include <library/cpp/protobuf/json/proto/enum_options.pb.h>
  8. #include <util/generic/yexception.h>
  9. #include <util/string/ascii.h>
  10. #include <util/string/cast.h>
  11. #include <util/system/win_undef.h>
  12. namespace NProtobufJson {
  13. using namespace NProtoBuf;
  14. class TJsonKeyBuilder {
  15. public:
  16. TJsonKeyBuilder(const FieldDescriptor& field, const TProto2JsonConfig& config, TString& tmpBuf)
  17. : NewKeyStr(tmpBuf)
  18. {
  19. if (config.NameGenerator) {
  20. NewKeyStr = config.NameGenerator(field);
  21. NewKeyBuf = NewKeyStr;
  22. return;
  23. }
  24. if (config.UseJsonName) {
  25. Y_ASSERT(!field.json_name().empty());
  26. NewKeyStr = field.json_name();
  27. if (!field.has_json_name() && !NewKeyStr.empty()) {
  28. // FIXME: https://st.yandex-team.ru/CONTRIB-139
  29. NewKeyStr[0] = AsciiToLower(NewKeyStr[0]);
  30. }
  31. NewKeyBuf = NewKeyStr;
  32. return;
  33. }
  34. switch (config.FieldNameMode) {
  35. case TProto2JsonConfig::FieldNameOriginalCase: {
  36. NewKeyBuf = field.name();
  37. break;
  38. }
  39. case TProto2JsonConfig::FieldNameLowerCase: {
  40. NewKeyStr = field.name();
  41. NewKeyStr.to_lower();
  42. NewKeyBuf = NewKeyStr;
  43. break;
  44. }
  45. case TProto2JsonConfig::FieldNameUpperCase: {
  46. NewKeyStr = field.name();
  47. NewKeyStr.to_upper();
  48. NewKeyBuf = NewKeyStr;
  49. break;
  50. }
  51. case TProto2JsonConfig::FieldNameCamelCase: {
  52. NewKeyStr = field.name();
  53. if (!NewKeyStr.empty()) {
  54. NewKeyStr[0] = AsciiToLower(NewKeyStr[0]);
  55. }
  56. NewKeyBuf = NewKeyStr;
  57. break;
  58. }
  59. case TProto2JsonConfig::FieldNameSnakeCase: {
  60. NewKeyStr = field.name();
  61. ToSnakeCase(&NewKeyStr);
  62. NewKeyBuf = NewKeyStr;
  63. break;
  64. }
  65. case TProto2JsonConfig::FieldNameSnakeCaseDense: {
  66. NewKeyStr = field.name();
  67. ToSnakeCaseDense(&NewKeyStr);
  68. NewKeyBuf = NewKeyStr;
  69. break;
  70. }
  71. default:
  72. Y_DEBUG_ABORT_UNLESS(false, "Unknown FieldNameMode.");
  73. }
  74. }
  75. const TStringBuf& GetKey() const {
  76. return NewKeyBuf;
  77. }
  78. private:
  79. TStringBuf NewKeyBuf;
  80. TString& NewKeyStr;
  81. };
  82. TProto2JsonPrinter::TProto2JsonPrinter(const TProto2JsonConfig& cfg)
  83. : Config(cfg)
  84. {
  85. }
  86. TProto2JsonPrinter::~TProto2JsonPrinter() {
  87. }
  88. TStringBuf TProto2JsonPrinter::MakeKey(const FieldDescriptor& field) {
  89. return TJsonKeyBuilder(field, GetConfig(), TmpBuf).GetKey();
  90. }
  91. template <bool InMapContext, typename T>
  92. std::enable_if_t<InMapContext, void> WriteWithMaybeEmptyKey(IJsonOutput& json, const TStringBuf& key, const T& value) {
  93. json.WriteKey(key).Write(value);
  94. }
  95. template <bool InMapContext, typename T>
  96. std::enable_if_t<!InMapContext, void> WriteWithMaybeEmptyKey(IJsonOutput& array, const TStringBuf& key, const T& value) {
  97. Y_ASSERT(!key);
  98. array.Write(value);
  99. }
  100. template <bool InMapContext>
  101. Y_NO_INLINE void TProto2JsonPrinter::PrintStringValue(const FieldDescriptor& field,
  102. const TStringBuf& key, const TString& value,
  103. IJsonOutput& json) {
  104. if (!GetConfig().StringTransforms.empty()) {
  105. TString tmpBuf = value;
  106. for (const TStringTransformPtr& stringTransform : GetConfig().StringTransforms) {
  107. Y_ASSERT(stringTransform);
  108. if (stringTransform) {
  109. if (field.type() == FieldDescriptor::TYPE_BYTES)
  110. stringTransform->TransformBytes(tmpBuf);
  111. else
  112. stringTransform->Transform(tmpBuf);
  113. }
  114. }
  115. WriteWithMaybeEmptyKey<InMapContext>(json, key, tmpBuf);
  116. } else {
  117. WriteWithMaybeEmptyKey<InMapContext>(json, key, value);
  118. }
  119. }
  120. template <bool InMapContext>
  121. void TProto2JsonPrinter::PrintEnumValue(const TStringBuf& key,
  122. const EnumValueDescriptor* value,
  123. IJsonOutput& json) {
  124. if (Config.EnumValueGenerator) {
  125. WriteWithMaybeEmptyKey<InMapContext>(json, key, Config.EnumValueGenerator(*value));
  126. return;
  127. }
  128. if (Config.UseJsonEnumValue) {
  129. auto jsonEnumValue = value->options().GetExtension(json_enum_value);
  130. if (!jsonEnumValue) {
  131. ythrow yexception() << "Trying to using json enum value for field " << value->name() << " which is not set.";
  132. }
  133. WriteWithMaybeEmptyKey<InMapContext>(json, key, jsonEnumValue);
  134. return;
  135. }
  136. switch (GetConfig().EnumMode) {
  137. case TProto2JsonConfig::EnumNumber: {
  138. WriteWithMaybeEmptyKey<InMapContext>(json, key, value->number());
  139. break;
  140. }
  141. case TProto2JsonConfig::EnumName: {
  142. WriteWithMaybeEmptyKey<InMapContext>(json, key, value->name());
  143. break;
  144. }
  145. case TProto2JsonConfig::EnumFullName: {
  146. WriteWithMaybeEmptyKey<InMapContext>(json, key, value->full_name());
  147. break;
  148. }
  149. case TProto2JsonConfig::EnumNameLowerCase: {
  150. TString newName = value->name();
  151. newName.to_lower();
  152. WriteWithMaybeEmptyKey<InMapContext>(json, key, newName);
  153. break;
  154. }
  155. case TProto2JsonConfig::EnumFullNameLowerCase: {
  156. TString newName = value->full_name();
  157. newName.to_lower();
  158. WriteWithMaybeEmptyKey<InMapContext>(json, key, newName);
  159. break;
  160. }
  161. default:
  162. Y_DEBUG_ABORT_UNLESS(false, "Unknown EnumMode.");
  163. }
  164. }
  165. bool HandleTimeConversion(const Message& proto, IJsonOutput& json) {
  166. using namespace google::protobuf;
  167. auto type = proto.GetDescriptor()->well_known_type();
  168. // XXX static_cast will cause UB if used with dynamic messages
  169. // (can be created by a DynamicMessageFactory with SetDelegateToGeneratedFactory(false). Unlikely, but still possible).
  170. // See workaround with CopyFrom in JsonString2Duration, JsonString2Timestamp (json2proto.cpp)
  171. if (type == Descriptor::WellKnownType::WELLKNOWNTYPE_DURATION) {
  172. const auto& duration = static_cast<const Duration&>(proto);
  173. json.Write(util::TimeUtil::ToString(duration));
  174. return true;
  175. } else if (type == Descriptor::WellKnownType::WELLKNOWNTYPE_TIMESTAMP) {
  176. const auto& timestamp = static_cast<const Timestamp&>(proto);
  177. json.Write(util::TimeUtil::ToString(timestamp));
  178. return true;
  179. }
  180. return false;
  181. }
  182. bool TProto2JsonPrinter::TryPrintAny(const Message& proto, IJsonOutput& json) {
  183. using namespace google::protobuf;
  184. const FieldDescriptor* typeUrlField;
  185. const FieldDescriptor* valueField;
  186. if (!Any::GetAnyFieldDescriptors(proto, &typeUrlField, &valueField)) {
  187. return false;
  188. }
  189. const Reflection* const reflection = proto.GetReflection();
  190. const TString& typeUrl = reflection->GetString(proto, typeUrlField);
  191. TString fullTypeName;
  192. if (!Any::ParseAnyTypeUrl(typeUrl, &fullTypeName)) {
  193. return false;
  194. }
  195. const Descriptor* const valueDesc = proto.GetDescriptor()->file()->pool()->FindMessageTypeByName(fullTypeName);
  196. if (!valueDesc) {
  197. return false;
  198. }
  199. DynamicMessageFactory factory;
  200. const THolder<Message> valueMessage{factory.GetPrototype(valueDesc)->New()};
  201. const TString& serializedValue = reflection->GetString(proto, valueField);
  202. if (!valueMessage->ParseFromString(serializedValue)) {
  203. return false;
  204. }
  205. json.BeginObject();
  206. json.WriteKey("@type").Write(typeUrl);
  207. PrintFields(*valueMessage, json);
  208. json.EndObject();
  209. return true;
  210. }
  211. void TProto2JsonPrinter::PrintSingleField(const Message& proto,
  212. const FieldDescriptor& field,
  213. IJsonOutput& json,
  214. TStringBuf key,
  215. bool inProtoMap) {
  216. Y_ABORT_UNLESS(!field.is_repeated(), "field is repeated.");
  217. if (!key) {
  218. key = MakeKey(field);
  219. }
  220. #define FIELD_TO_JSON(EProtoCppType, ProtoGet) \
  221. case FieldDescriptor::EProtoCppType: { \
  222. json.WriteKey(key).Write(reflection->ProtoGet(proto, &field)); \
  223. break; \
  224. }
  225. #define INT_FIELD_TO_JSON(EProtoCppType, ProtoGet) \
  226. case FieldDescriptor::EProtoCppType: { \
  227. const auto value = reflection->ProtoGet(proto, &field); \
  228. if (NeedStringifyNumber(value)) { \
  229. json.WriteKey(key).Write(ToString(value)); \
  230. } else { \
  231. json.WriteKey(key).Write(value); \
  232. } \
  233. break; \
  234. }
  235. const Reflection* reflection = proto.GetReflection();
  236. bool shouldPrintField = inProtoMap || reflection->HasField(proto, &field);
  237. if (!shouldPrintField && GetConfig().MissingSingleKeyMode == TProto2JsonConfig::MissingKeyExplicitDefaultThrowRequired) {
  238. if (field.has_default_value()) {
  239. shouldPrintField = true;
  240. } else if (field.is_required()) {
  241. ythrow yexception() << "Empty required protobuf field: "
  242. << field.full_name() << ".";
  243. }
  244. }
  245. shouldPrintField = shouldPrintField ||
  246. (GetConfig().MissingSingleKeyMode == TProto2JsonConfig::MissingKeyDefault && !field.containing_oneof());
  247. if (shouldPrintField) {
  248. switch (field.cpp_type()) {
  249. INT_FIELD_TO_JSON(CPPTYPE_INT32, GetInt32);
  250. INT_FIELD_TO_JSON(CPPTYPE_INT64, GetInt64);
  251. INT_FIELD_TO_JSON(CPPTYPE_UINT32, GetUInt32);
  252. INT_FIELD_TO_JSON(CPPTYPE_UINT64, GetUInt64);
  253. FIELD_TO_JSON(CPPTYPE_DOUBLE, GetDouble);
  254. FIELD_TO_JSON(CPPTYPE_FLOAT, GetFloat);
  255. FIELD_TO_JSON(CPPTYPE_BOOL, GetBool);
  256. case FieldDescriptor::CPPTYPE_MESSAGE: {
  257. json.WriteKey(key);
  258. if (Config.ConvertTimeAsString && HandleTimeConversion(reflection->GetMessage(proto, &field), json)) {
  259. break;
  260. }
  261. const Message& msg = reflection->GetMessage(proto, &field);
  262. if (Config.ConvertAny && TryPrintAny(msg, json)) {
  263. break;
  264. }
  265. Print(msg, json);
  266. break;
  267. }
  268. case FieldDescriptor::CPPTYPE_ENUM: {
  269. PrintEnumValue<true>(key, reflection->GetEnum(proto, &field), json);
  270. break;
  271. }
  272. case FieldDescriptor::CPPTYPE_STRING: {
  273. TString scratch;
  274. const TString& value = reflection->GetStringReference(proto, &field, &scratch);
  275. PrintStringValue<true>(field, key, value, json);
  276. break;
  277. }
  278. default:
  279. ythrow yexception() << "Unknown protobuf field type: "
  280. << static_cast<int>(field.cpp_type()) << ".";
  281. }
  282. } else {
  283. switch (GetConfig().MissingSingleKeyMode) {
  284. case TProto2JsonConfig::MissingKeyNull: {
  285. json.WriteKey(key).WriteNull();
  286. break;
  287. }
  288. case TProto2JsonConfig::MissingKeySkip:
  289. case TProto2JsonConfig::MissingKeyExplicitDefaultThrowRequired:
  290. default:
  291. break;
  292. }
  293. }
  294. #undef FIELD_TO_JSON
  295. }
  296. void TProto2JsonPrinter::PrintRepeatedField(const Message& proto,
  297. const FieldDescriptor& field,
  298. IJsonOutput& json,
  299. TStringBuf key) {
  300. Y_ABORT_UNLESS(field.is_repeated(), "field isn't repeated.");
  301. const bool isMap = field.is_map() && GetConfig().MapAsObject;
  302. if (!key) {
  303. key = MakeKey(field);
  304. }
  305. #define REPEATED_FIELD_TO_JSON(EProtoCppType, ProtoGet) \
  306. case FieldDescriptor::EProtoCppType: { \
  307. for (size_t i = 0, endI = reflection->FieldSize(proto, &field); i < endI; ++i) \
  308. json.Write(reflection->ProtoGet(proto, &field, i)); \
  309. break; \
  310. }
  311. const Reflection* reflection = proto.GetReflection();
  312. if (reflection->FieldSize(proto, &field) > 0) {
  313. json.WriteKey(key);
  314. if (isMap) {
  315. json.BeginObject();
  316. } else {
  317. json.BeginList();
  318. }
  319. switch (field.cpp_type()) {
  320. REPEATED_FIELD_TO_JSON(CPPTYPE_INT32, GetRepeatedInt32);
  321. REPEATED_FIELD_TO_JSON(CPPTYPE_INT64, GetRepeatedInt64);
  322. REPEATED_FIELD_TO_JSON(CPPTYPE_UINT32, GetRepeatedUInt32);
  323. REPEATED_FIELD_TO_JSON(CPPTYPE_UINT64, GetRepeatedUInt64);
  324. REPEATED_FIELD_TO_JSON(CPPTYPE_DOUBLE, GetRepeatedDouble);
  325. REPEATED_FIELD_TO_JSON(CPPTYPE_FLOAT, GetRepeatedFloat);
  326. REPEATED_FIELD_TO_JSON(CPPTYPE_BOOL, GetRepeatedBool);
  327. case FieldDescriptor::CPPTYPE_MESSAGE: {
  328. if (isMap) {
  329. for (size_t i = 0, endI = reflection->FieldSize(proto, &field); i < endI; ++i) {
  330. PrintKeyValue(reflection->GetRepeatedMessage(proto, &field, i), json);
  331. }
  332. } else {
  333. for (size_t i = 0, endI = reflection->FieldSize(proto, &field); i < endI; ++i) {
  334. Print(reflection->GetRepeatedMessage(proto, &field, i), json);
  335. }
  336. }
  337. break;
  338. }
  339. case FieldDescriptor::CPPTYPE_ENUM: {
  340. for (int i = 0, endI = reflection->FieldSize(proto, &field); i < endI; ++i)
  341. PrintEnumValue<false>(TStringBuf(), reflection->GetRepeatedEnum(proto, &field, i), json);
  342. break;
  343. }
  344. case FieldDescriptor::CPPTYPE_STRING: {
  345. TString scratch;
  346. for (int i = 0, endI = reflection->FieldSize(proto, &field); i < endI; ++i) {
  347. const TString& value =
  348. reflection->GetRepeatedStringReference(proto, &field, i, &scratch);
  349. PrintStringValue<false>(field, TStringBuf(), value, json);
  350. }
  351. break;
  352. }
  353. default:
  354. ythrow yexception() << "Unknown protobuf field type: "
  355. << static_cast<int>(field.cpp_type()) << ".";
  356. }
  357. if (isMap) {
  358. json.EndObject();
  359. } else {
  360. json.EndList();
  361. }
  362. } else {
  363. switch (GetConfig().MissingRepeatedKeyMode) {
  364. case TProto2JsonConfig::MissingKeyNull: {
  365. json.WriteKey(key).WriteNull();
  366. break;
  367. }
  368. case TProto2JsonConfig::MissingKeyDefault: {
  369. json.WriteKey(key);
  370. if (isMap) {
  371. json.BeginObject().EndObject();
  372. } else {
  373. json.BeginList().EndList();
  374. }
  375. break;
  376. }
  377. case TProto2JsonConfig::MissingKeySkip:
  378. case TProto2JsonConfig::MissingKeyExplicitDefaultThrowRequired:
  379. default:
  380. break;
  381. }
  382. }
  383. #undef REPEATED_FIELD_TO_JSON
  384. }
  385. void TProto2JsonPrinter::PrintKeyValue(const NProtoBuf::Message& proto,
  386. IJsonOutput& json) {
  387. const FieldDescriptor* keyField = proto.GetDescriptor()->map_key();
  388. Y_ABORT_UNLESS(keyField, "Map entry key field not found.");
  389. TString key = MakeKey(proto, *keyField);
  390. const FieldDescriptor* valueField = proto.GetDescriptor()->map_value();
  391. Y_ABORT_UNLESS(valueField, "Map entry value field not found.");
  392. PrintSingleField(proto, *valueField, json, key, true);
  393. }
  394. TString TProto2JsonPrinter::MakeKey(const NProtoBuf::Message& proto,
  395. const NProtoBuf::FieldDescriptor& field) {
  396. const Reflection* reflection = proto.GetReflection();
  397. TString result;
  398. switch (field.cpp_type()) {
  399. case FieldDescriptor::CPPTYPE_INT32:
  400. result = ToString(reflection->GetInt32(proto, &field));
  401. break;
  402. case FieldDescriptor::CPPTYPE_INT64:
  403. result = ToString(reflection->GetInt64(proto, &field));
  404. break;
  405. case FieldDescriptor::CPPTYPE_UINT32:
  406. result = ToString(reflection->GetUInt32(proto, &field));
  407. break;
  408. case FieldDescriptor::CPPTYPE_UINT64:
  409. result = ToString(reflection->GetUInt64(proto, &field));
  410. break;
  411. case FieldDescriptor::CPPTYPE_DOUBLE:
  412. result = ToString(reflection->GetDouble(proto, &field));
  413. break;
  414. case FieldDescriptor::CPPTYPE_FLOAT:
  415. result = ToString(reflection->GetFloat(proto, &field));
  416. break;
  417. case FieldDescriptor::CPPTYPE_BOOL:
  418. result = ToString(reflection->GetBool(proto, &field));
  419. break;
  420. case FieldDescriptor::CPPTYPE_ENUM: {
  421. const EnumValueDescriptor* value = reflection->GetEnum(proto, &field);
  422. switch (GetConfig().EnumMode) {
  423. case TProto2JsonConfig::EnumNumber:
  424. result = ToString(value->number());
  425. break;
  426. case TProto2JsonConfig::EnumName:
  427. result = value->name();
  428. break;
  429. case TProto2JsonConfig::EnumFullName:
  430. result = value->full_name();
  431. break;
  432. case TProto2JsonConfig::EnumNameLowerCase:
  433. result = value->name();
  434. result.to_lower();
  435. break;
  436. case TProto2JsonConfig::EnumFullNameLowerCase:
  437. result = value->full_name();
  438. result.to_lower();
  439. break;
  440. default:
  441. ythrow yexception() << "Unsupported enum mode.";
  442. }
  443. break;
  444. }
  445. case FieldDescriptor::CPPTYPE_STRING:
  446. result = reflection->GetString(proto, &field);
  447. break;
  448. default:
  449. ythrow yexception() << "Unsupported key type.";
  450. }
  451. return result;
  452. }
  453. void TProto2JsonPrinter::PrintField(const Message& proto,
  454. const FieldDescriptor& field,
  455. IJsonOutput& json,
  456. const TStringBuf key) {
  457. if (field.is_repeated())
  458. PrintRepeatedField(proto, field, json, key);
  459. else
  460. PrintSingleField(proto, field, json, key);
  461. }
  462. void TProto2JsonPrinter::PrintFields(const Message& proto, IJsonOutput& json) {
  463. const Descriptor* descriptor = proto.GetDescriptor();
  464. Y_ASSERT(descriptor);
  465. // Iterate over all non-extension fields
  466. for (int f = 0, endF = descriptor->field_count(); f < endF; ++f) {
  467. const FieldDescriptor* field = descriptor->field(f);
  468. Y_ASSERT(field);
  469. PrintField(proto, *field, json);
  470. }
  471. // Check extensions via ListFields
  472. std::vector<const FieldDescriptor*> fields;
  473. auto* ref = proto.GetReflection();
  474. ref->ListFields(proto, &fields);
  475. for (const FieldDescriptor* field : fields) {
  476. Y_ASSERT(field);
  477. if (field->is_extension()) {
  478. switch (GetConfig().ExtensionFieldNameMode) {
  479. case TProto2JsonConfig::ExtFldNameFull:
  480. PrintField(proto, *field, json, field->full_name());
  481. break;
  482. case TProto2JsonConfig::ExtFldNameShort:
  483. PrintField(proto, *field, json);
  484. break;
  485. }
  486. }
  487. }
  488. }
  489. void TProto2JsonPrinter::Print(const Message& proto, IJsonOutput& json, bool closeMap) {
  490. json.BeginObject();
  491. PrintFields(proto, json);
  492. if (closeMap) {
  493. json.EndObject();
  494. }
  495. }
  496. template <class T, class U>
  497. std::enable_if_t<!std::is_unsigned<T>::value, bool> ValueInRange(T value, U range) {
  498. return value > -range && value < range;
  499. }
  500. template <class T, class U>
  501. std::enable_if_t<std::is_unsigned<T>::value, bool> ValueInRange(T value, U range) {
  502. return value < (std::make_unsigned_t<U>)(range);
  503. }
  504. template <class T>
  505. bool TProto2JsonPrinter::NeedStringifyNumber(T value) const {
  506. constexpr long SAFE_INTEGER_RANGE_FLOAT = 1L << 24;
  507. constexpr long long SAFE_INTEGER_RANGE_DOUBLE = 1LL << 53;
  508. switch (GetConfig().StringifyNumbers) {
  509. case TProto2JsonConfig::StringifyLongNumbersNever:
  510. return false;
  511. case TProto2JsonConfig::StringifyLongNumbersForFloat:
  512. return !ValueInRange(value, SAFE_INTEGER_RANGE_FLOAT);
  513. case TProto2JsonConfig::StringifyLongNumbersForDouble:
  514. return !ValueInRange(value, SAFE_INTEGER_RANGE_DOUBLE);
  515. case TProto2JsonConfig::StringifyInt64Always:
  516. return std::is_same_v<T, i64> || std::is_same_v<T, ui64>;
  517. }
  518. return false;
  519. }
  520. }