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