proto2json_printer.cpp 20 KB

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