proto2json_printer.cpp 22 KB

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