inline.h 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. #pragma once
  2. // A printer from protobuf to json string, with ability to inline some string fields of given protobuf message
  3. // into output as ready json without additional escaping. These fields should be marked using special field option.
  4. // An example of usage:
  5. // 1) Define a field option in your .proto to identify fields which should be inlined, e.g.
  6. //
  7. // import "google/protobuf/descriptor.proto";
  8. // extend google.protobuf.FieldOptions {
  9. // optional bool this_is_json = 58253; // do not forget assign some more or less unique tag
  10. // }
  11. //
  12. // 2) Mark some fields of your protobuf message with this option, e.g.:
  13. //
  14. // message TMyObject {
  15. // optional string A = 1 [(this_is_json) = true];
  16. // }
  17. //
  18. // 3) In the C++ code you prepare somehow an object of TMyObject type
  19. //
  20. // TMyObject o;
  21. // o.Set("{\"inner\":\"value\"}");
  22. //
  23. // 4) And then serialize it to json string with inlining, e.g.:
  24. //
  25. // Cout << NProtobufJson::PrintInlined(o, MakeFieldOptionFunctor(this_is_json)) << Endl;
  26. //
  27. // 5) Alternatively you can specify a some more abstract functor for defining raw json fields
  28. //
  29. // which will print following json to stdout:
  30. // {"A":{"inner":"value"}}
  31. // instead of
  32. // {"A":"{\"inner\":\"value\"}"}
  33. // which would be printed with normal Proto2Json printer.
  34. //
  35. // See ut/inline_ut.cpp for additional examples of usage.
  36. #include "config.h"
  37. #include "proto2json_printer.h"
  38. #include "json_output_create.h"
  39. #include <library/cpp/protobuf/util/simple_reflection.h>
  40. #include <util/generic/maybe.h>
  41. #include <util/generic/yexception.h>
  42. #include <util/generic/utility.h>
  43. #include <functional>
  44. namespace NProtobufJson {
  45. template <typename TBasePrinter = TProto2JsonPrinter> // TBasePrinter is assumed to be a TProto2JsonPrinter descendant
  46. class TInliningPrinter: public TBasePrinter {
  47. public:
  48. using TFieldPredicate = std::function<bool(const NProtoBuf::Message&,
  49. const NProtoBuf::FieldDescriptor*)>;
  50. template <typename... TArgs>
  51. TInliningPrinter(TFieldPredicate isInlined, TArgs&&... args)
  52. : TBasePrinter(std::forward<TArgs>(args)...)
  53. , IsInlined(std::move(isInlined))
  54. {
  55. }
  56. virtual void PrintField(const NProtoBuf::Message& proto,
  57. const NProtoBuf::FieldDescriptor& field,
  58. IJsonOutput& json,
  59. TStringBuf key) override {
  60. const NProtoBuf::TConstField f(proto, &field);
  61. if (!key && IsInlined(proto, &field) && ShouldPrint(f)) {
  62. key = this->MakeKey(field);
  63. json.WriteKey(key);
  64. if (!field.is_repeated()) {
  65. json.WriteRawJson(f.Get<TString>());
  66. } else {
  67. json.BeginList();
  68. for (size_t i = 0, sz = f.Size(); i < sz; ++i)
  69. json.WriteRawJson(f.Get<TString>(i));
  70. json.EndList();
  71. }
  72. } else {
  73. TBasePrinter::PrintField(proto, field, json, key);
  74. }
  75. }
  76. private:
  77. bool ShouldPrint(const NProtoBuf::TConstField& f) const {
  78. if (!f.IsString())
  79. ythrow yexception() << "TInliningPrinter: json field "
  80. << f.Field()->name() << " should be a string";
  81. if (f.HasValue())
  82. return true;
  83. // we may want write default value for given field in case of its absence
  84. const auto& cfg = this->GetConfig();
  85. return (f.Field()->is_repeated() ? cfg.MissingRepeatedKeyMode : cfg.MissingSingleKeyMode) == TProto2JsonConfig::MissingKeyDefault;
  86. }
  87. private:
  88. TFieldPredicate IsInlined;
  89. };
  90. inline void PrintInlined(const NProtoBuf::Message& msg, TInliningPrinter<>::TFieldPredicate isInlined, IJsonOutput& output, const TProto2JsonConfig& config = TProto2JsonConfig()) {
  91. TInliningPrinter<> printer(std::move(isInlined), config);
  92. printer.Print(msg, output);
  93. }
  94. inline TString PrintInlined(const NProtoBuf::Message& msg, TInliningPrinter<>::TFieldPredicate isInlined, const TProto2JsonConfig& config = TProto2JsonConfig()) {
  95. TString ret;
  96. PrintInlined(msg, std::move(isInlined), *CreateJsonMapOutput(ret, config), config);
  97. return ret;
  98. }
  99. }