Coverage Report

Created: 2025-10-12 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/brpc/src/json2pb/pb_to_json.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include <iostream>
19
#include <vector>
20
#include <string>
21
#include <sstream>
22
#include <sys/time.h>
23
#include <time.h>
24
#include <google/protobuf/descriptor.h>
25
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
26
#include <gflags/gflags.h>
27
#include "json2pb/zero_copy_stream_writer.h"
28
#include "json2pb/encode_decode.h"
29
#include "json2pb/protobuf_map.h"
30
#include "json2pb/rapidjson.h"
31
#include "json2pb/pb_to_json.h"
32
#include "json2pb/protobuf_type_resolver.h"
33
#include "butil/iobuf.h"
34
#include "butil/base64.h"
35
36
namespace json2pb {
37
38
DECLARE_int32(json2pb_max_recursion_depth);
39
40
// Helper function to check if the maximum depth of a message is exceeded.
41
0
bool ExceedMaxDepth(const google::protobuf::Message& message, int current_depth) {
42
0
    if (current_depth > FLAGS_json2pb_max_recursion_depth) {
43
0
        return true;
44
0
    }
45
46
0
    const google::protobuf::Descriptor* descriptor = message.GetDescriptor();
47
0
    const google::protobuf::Reflection* reflection = message.GetReflection();
48
49
0
    std::vector<const google::protobuf::FieldDescriptor*> fields;
50
    // Collect declared fields.
51
0
    for (int i = 0; i < descriptor->field_count(); ++i) {
52
0
        fields.push_back(descriptor->field(i));
53
0
    }
54
    // Collect extension fields (if any).
55
0
    {
56
0
        std::vector<const google::protobuf::FieldDescriptor*> ext_fields;
57
0
        descriptor->file()->pool()->FindAllExtensions(descriptor, &ext_fields);
58
0
        fields.insert(fields.end(), ext_fields.begin(), ext_fields.end());
59
0
    }
60
61
0
    for (const auto* field : fields) {
62
0
        if (field->cpp_type() != google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
63
0
            continue;
64
0
        }
65
66
0
        if (field->is_repeated()) {
67
0
            const int count = reflection->FieldSize(message, field);
68
0
            for (int j = 0; j < count; ++j) {
69
0
                const google::protobuf::Message& sub_message =
70
0
                    reflection->GetRepeatedMessage(message, field, j);
71
0
                if (ExceedMaxDepth(sub_message, current_depth + 1)) {
72
0
                    return true;
73
0
                }
74
0
            }
75
0
        } else {
76
0
            if (reflection->HasField(message, field)) {
77
0
                const google::protobuf::Message& sub_message =
78
0
                    reflection->GetMessage(message, field);
79
0
                if (ExceedMaxDepth(sub_message, current_depth + 1)) {
80
0
                    return true;
81
0
                }
82
0
            }
83
0
        }
84
0
    }
85
0
    return false;
86
0
}
87
88
Pb2JsonOptions::Pb2JsonOptions()
89
0
    : enum_option(OUTPUT_ENUM_BY_NAME)
90
0
    , pretty_json(false)
91
0
    , enable_protobuf_map(true)
92
#ifdef BAIDU_INTERNAL
93
    , bytes_to_base64(false)
94
#else
95
0
    , bytes_to_base64(true)
96
#endif
97
0
    , jsonify_empty_array(false)
98
0
    , always_print_primitive_fields(false)
99
0
    , single_repeated_to_array(false) {
100
0
}
101
102
class PbToJsonConverter {
103
public:
104
0
    explicit PbToJsonConverter(const Pb2JsonOptions& opt) : _option(opt) {}
105
106
    template <typename Handler>
107
    bool Convert(const google::protobuf::Message& message, Handler& handler, int depth = 0);
108
109
0
    const std::string& ErrorText() const { return _error; }
110
111
private:
112
    template <typename Handler>
113
    bool _PbFieldToJson(const google::protobuf::Message& message,
114
                        const google::protobuf::FieldDescriptor* field,
115
                        Handler& handler, int depth);
116
117
    std::string _error;
118
    Pb2JsonOptions _option;
119
};
120
121
template <typename Handler>
122
0
bool PbToJsonConverter::Convert(const google::protobuf::Message& message, Handler& handler, int depth) {
123
0
    if (depth > FLAGS_json2pb_max_recursion_depth) {
124
0
        _error = "Exceeded maximum recursion depth";
125
0
        return false;
126
0
    }
127
0
    const google::protobuf::Reflection* reflection = message.GetReflection();
128
0
    const google::protobuf::Descriptor* descriptor = message.GetDescriptor();
129
130
0
    int ext_range_count = descriptor->extension_range_count();
131
0
    int field_count = descriptor->field_count();
132
0
    std::vector<const google::protobuf::FieldDescriptor*> fields;
133
0
    fields.reserve(64);
134
0
    for (int i = 0; i < ext_range_count; ++i) {
135
0
        const google::protobuf::Descriptor::ExtensionRange*
136
0
            ext_range = descriptor->extension_range(i);
137
0
#if GOOGLE_PROTOBUF_VERSION < 4025000
138
0
        for (int tag_number = ext_range->start; tag_number < ext_range->end; ++tag_number)
139
#else
140
        for (int tag_number = ext_range->start_number(); tag_number < ext_range->end_number(); ++tag_number)
141
#endif
142
0
        {
143
0
            const google::protobuf::FieldDescriptor* field =
144
0
                    reflection->FindKnownExtensionByNumber(tag_number);
145
0
            if (field) {
146
0
                fields.push_back(field);
147
0
            }
148
0
        }
149
0
    }
150
0
    std::vector<const google::protobuf::FieldDescriptor*> map_fields;
151
0
    for (int i = 0; i < field_count; ++i) {
152
0
        const google::protobuf::FieldDescriptor* field = descriptor->field(i);
153
0
        if (_option.enable_protobuf_map && json2pb::IsProtobufMap(field)) {
154
0
            map_fields.push_back(field);
155
0
        } else {
156
0
            fields.push_back(field);
157
0
        }
158
0
    }
159
160
0
    if (depth == 0 && _option.single_repeated_to_array) {
161
0
        if (map_fields.empty() && fields.size() == 1 && fields.front()->is_repeated()) {
162
0
            return _PbFieldToJson(message, fields.front(), handler, depth);
163
0
        }
164
0
    }
165
166
0
    handler.StartObject();
167
168
    // Fill in non-map fields
169
0
    std::string field_name_str;
170
0
    for (size_t i = 0; i < fields.size(); ++i) {
171
0
        const google::protobuf::FieldDescriptor* field = fields[i];
172
0
        if (!field->is_repeated() && !reflection->HasField(message, field)) {
173
            // Field that has not been set
174
0
            if (field->is_required()) {
175
0
                _error = "Missing required field: " + field->full_name();
176
0
                return false;
177
0
            }
178
            // Whether dumps default fields
179
0
            if (!_option.always_print_primitive_fields) {
180
0
                continue;
181
0
            }
182
0
        } else if (field->is_repeated()
183
0
                   && reflection->FieldSize(message, field) == 0
184
0
                   && !_option.jsonify_empty_array) {
185
            // Repeated field that has no entry
186
0
            continue;
187
0
        }
188
189
0
        const std::string& orig_name = field->name();
190
0
        bool decoded = decode_name(orig_name, field_name_str); 
191
0
        const std::string& name = decoded ? field_name_str : orig_name;
192
0
        handler.Key(name.data(), name.size(), false);
193
0
        if (!_PbFieldToJson(message, field, handler, depth)) {
194
0
            return false;
195
0
        }
196
0
    }
197
198
    // Fill in map fields
199
0
    for (size_t i = 0; i < map_fields.size(); ++i) {
200
0
        const google::protobuf::FieldDescriptor* map_desc = map_fields[i];
201
0
        const google::protobuf::FieldDescriptor* key_desc =
202
0
                map_desc->message_type()->field(json2pb::KEY_INDEX);
203
0
        const google::protobuf::FieldDescriptor* value_desc =
204
0
                map_desc->message_type()->field(json2pb::VALUE_INDEX);
205
206
        // Write a json object corresponding to hold protobuf map
207
        // such as {"key": value, ...}
208
0
        const std::string& orig_name = map_desc->name();
209
0
        bool decoded = decode_name(orig_name, field_name_str);
210
0
        const std::string& name = decoded ? field_name_str : orig_name;
211
0
        handler.Key(name.data(), name.size(), false);
212
0
        handler.StartObject();
213
0
        std::string entry_name;
214
0
        for (int j = 0; j < reflection->FieldSize(message, map_desc); ++j) {
215
0
            const google::protobuf::Message& entry =
216
0
                    reflection->GetRepeatedMessage(message, map_desc, j);
217
0
            const google::protobuf::Reflection* entry_reflection = entry.GetReflection();
218
0
            entry_name = entry_reflection->GetStringReference(
219
0
                entry, key_desc, &entry_name);
220
0
            handler.Key(entry_name.data(), entry_name.size(), false);
221
222
            // Fill in entries into this json object
223
0
            if (!_PbFieldToJson(entry, value_desc, handler, depth)) {
224
0
                return false;
225
0
            }
226
0
        }
227
        // Hack: Pass 0 as parameter since Writer doesn't care this
228
0
        handler.EndObject(0);
229
0
    }
230
    // Hack: Pass 0 as parameter since Writer doesn't care this
231
0
    handler.EndObject(0);
232
0
    return true;
233
0
}
Unexecuted instantiation: bool json2pb::PbToJsonConverter::Convert<butil::rapidjson::PrettyWriter<butil::rapidjson::GenericStringBuffer<butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator>, butil::rapidjson::UTF8<char>, butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator> >(google::protobuf::Message const&, butil::rapidjson::PrettyWriter<butil::rapidjson::GenericStringBuffer<butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator>, butil::rapidjson::UTF8<char>, butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator>&, int)
Unexecuted instantiation: bool json2pb::PbToJsonConverter::Convert<butil::rapidjson::OptimizedWriter<butil::rapidjson::GenericStringBuffer<butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator>, butil::rapidjson::UTF8<char>, butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator> >(google::protobuf::Message const&, butil::rapidjson::OptimizedWriter<butil::rapidjson::GenericStringBuffer<butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator>, butil::rapidjson::UTF8<char>, butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator>&, int)
Unexecuted instantiation: bool json2pb::PbToJsonConverter::Convert<butil::rapidjson::PrettyWriter<json2pb::ZeroCopyStreamWriter, butil::rapidjson::UTF8<char>, butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator> >(google::protobuf::Message const&, butil::rapidjson::PrettyWriter<json2pb::ZeroCopyStreamWriter, butil::rapidjson::UTF8<char>, butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator>&, int)
Unexecuted instantiation: bool json2pb::PbToJsonConverter::Convert<butil::rapidjson::OptimizedWriter<json2pb::ZeroCopyStreamWriter, butil::rapidjson::UTF8<char>, butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator> >(google::protobuf::Message const&, butil::rapidjson::OptimizedWriter<json2pb::ZeroCopyStreamWriter, butil::rapidjson::UTF8<char>, butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator>&, int)
234
235
template <typename Handler>
236
bool PbToJsonConverter::_PbFieldToJson(
237
    const google::protobuf::Message& message,
238
    const google::protobuf::FieldDescriptor* field,
239
0
    Handler& handler, int depth) {
240
0
    const google::protobuf::Reflection* reflection = message.GetReflection();
241
0
    switch (field->cpp_type()) {
242
0
#define CASE_FIELD_TYPE(cpptype, method, valuetype, handle)             \
243
0
    case google::protobuf::FieldDescriptor::CPPTYPE_##cpptype: {                          \
244
0
        if (field->is_repeated()) {                                     \
245
0
            int field_size = reflection->FieldSize(message, field);     \
246
0
            handler.StartArray();                                       \
247
0
            for (int index = 0; index < field_size; ++index) {          \
248
0
                handler.handle(static_cast<valuetype>(                  \
249
0
                    reflection->GetRepeated##method(                    \
250
0
                        message, field, index)));                       \
251
0
            }                                                           \
252
0
            handler.EndArray(field_size);                               \
253
0
                                                                        \
254
0
        } else {                                                        \
255
0
            handler.handle(static_cast<valuetype>(                      \
256
0
                reflection->Get##method(message, field)));              \
257
0
        }                                                               \
258
0
        break;                                                          \
259
0
    }
260
        
261
0
    CASE_FIELD_TYPE(BOOL,   Bool,   bool,         Bool);
262
0
    CASE_FIELD_TYPE(INT32,  Int32,  int,          AddInt);
263
0
    CASE_FIELD_TYPE(UINT32, UInt32, unsigned int, AddUint);
264
0
    CASE_FIELD_TYPE(INT64,  Int64,  int64_t,      AddInt64);
265
0
    CASE_FIELD_TYPE(UINT64, UInt64, uint64_t,     AddUint64);
266
0
    CASE_FIELD_TYPE(FLOAT,  Float,  double,       Double);
267
0
    CASE_FIELD_TYPE(DOUBLE, Double, double,       Double);
268
0
#undef CASE_FIELD_TYPE
269
270
0
    case google::protobuf::FieldDescriptor::CPPTYPE_STRING: {
271
0
        std::string value;
272
0
        if (field->is_repeated()) {
273
0
            int field_size = reflection->FieldSize(message, field);
274
0
            handler.StartArray();
275
0
            for (int index = 0; index < field_size; ++index) {
276
0
                value = reflection->GetRepeatedStringReference(
277
0
                    message, field, index, &value);
278
0
                if (field->type() == google::protobuf::FieldDescriptor::TYPE_BYTES
279
0
                    && _option.bytes_to_base64) {
280
0
                    std::string value_decoded;
281
0
                    butil::Base64Encode(value, &value_decoded);
282
0
                    handler.String(value_decoded.data(), value_decoded.size(), false);
283
0
                } else {
284
0
                    handler.String(value.data(), value.size(), false);
285
0
                }
286
0
            }
287
0
            handler.EndArray(field_size);
288
            
289
0
        } else {
290
0
            value = reflection->GetStringReference(message, field, &value);
291
0
            if (field->type() == google::protobuf::FieldDescriptor::TYPE_BYTES
292
0
                && _option.bytes_to_base64) {
293
0
                std::string value_decoded;
294
0
                butil::Base64Encode(value, &value_decoded);
295
0
                handler.String(value_decoded.data(), value_decoded.size(), false);
296
0
            } else {
297
0
                handler.String(value.data(), value.size(), false);
298
0
            }
299
0
        }
300
0
        break;
301
0
    }
302
303
0
    case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: {
304
0
        if (field->is_repeated()) {
305
0
            int field_size = reflection->FieldSize(message, field);
306
0
            handler.StartArray();
307
0
            if (_option.enum_option == OUTPUT_ENUM_BY_NAME) {
308
0
                for (int index = 0; index < field_size; ++index) { 
309
0
                    const std::string& enum_name = reflection->GetRepeatedEnum(
310
0
                        message, field, index)->name();
311
0
                    handler.String(enum_name.data(), enum_name.size(), false);
312
0
                }
313
0
            } else {
314
0
                for (int index = 0; index < field_size; ++index) { 
315
0
                    handler.AddInt(reflection->GetRepeatedEnum(
316
0
                        message, field, index)->number());
317
0
                }
318
0
            }
319
0
            handler.EndArray();
320
            
321
0
        } else {
322
0
            if (_option.enum_option == OUTPUT_ENUM_BY_NAME) {
323
0
                const std::string& enum_name =
324
0
                        reflection->GetEnum(message, field)->name();
325
0
                handler.String(enum_name.data(), enum_name.size(), false);
326
0
            } else {
327
0
                handler.AddInt(reflection->GetEnum(message, field)->number());
328
0
            }
329
0
        }
330
0
        break;
331
0
    }
332
333
0
    case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: {
334
0
        if (field->is_repeated()) {
335
0
            int field_size = reflection->FieldSize(message, field);
336
0
            handler.StartArray();
337
0
            for (int index = 0; index < field_size; ++index) {
338
0
                if (!Convert(reflection->GetRepeatedMessage(
339
0
                        message, field, index), handler, depth + 1)) {
340
0
                    return false;
341
0
                }
342
0
            }
343
0
            handler.EndArray(field_size);
344
            
345
0
        } else {
346
0
            if (!Convert(reflection->GetMessage(message, field), handler, depth + 1)) {
347
0
                return false;
348
0
            }
349
0
        }
350
0
        break;
351
0
    }
352
0
    }
353
0
    return true;
354
0
}
Unexecuted instantiation: bool json2pb::PbToJsonConverter::_PbFieldToJson<butil::rapidjson::PrettyWriter<butil::rapidjson::GenericStringBuffer<butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator>, butil::rapidjson::UTF8<char>, butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator> >(google::protobuf::Message const&, google::protobuf::FieldDescriptor const*, butil::rapidjson::PrettyWriter<butil::rapidjson::GenericStringBuffer<butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator>, butil::rapidjson::UTF8<char>, butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator>&, int)
Unexecuted instantiation: bool json2pb::PbToJsonConverter::_PbFieldToJson<butil::rapidjson::OptimizedWriter<butil::rapidjson::GenericStringBuffer<butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator>, butil::rapidjson::UTF8<char>, butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator> >(google::protobuf::Message const&, google::protobuf::FieldDescriptor const*, butil::rapidjson::OptimizedWriter<butil::rapidjson::GenericStringBuffer<butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator>, butil::rapidjson::UTF8<char>, butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator>&, int)
Unexecuted instantiation: bool json2pb::PbToJsonConverter::_PbFieldToJson<butil::rapidjson::PrettyWriter<json2pb::ZeroCopyStreamWriter, butil::rapidjson::UTF8<char>, butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator> >(google::protobuf::Message const&, google::protobuf::FieldDescriptor const*, butil::rapidjson::PrettyWriter<json2pb::ZeroCopyStreamWriter, butil::rapidjson::UTF8<char>, butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator>&, int)
Unexecuted instantiation: bool json2pb::PbToJsonConverter::_PbFieldToJson<butil::rapidjson::OptimizedWriter<json2pb::ZeroCopyStreamWriter, butil::rapidjson::UTF8<char>, butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator> >(google::protobuf::Message const&, google::protobuf::FieldDescriptor const*, butil::rapidjson::OptimizedWriter<json2pb::ZeroCopyStreamWriter, butil::rapidjson::UTF8<char>, butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator>&, int)
355
356
template <typename OutputStream>
357
bool ProtoMessageToJsonStream(const google::protobuf::Message& message,
358
                              const Pb2JsonOptions& options,
359
0
                              OutputStream& os, std::string* error) {
360
0
    PbToJsonConverter converter(options);
361
0
    bool succ = false;
362
0
    if (options.pretty_json) {
363
0
        BUTIL_RAPIDJSON_NAMESPACE::PrettyWriter<OutputStream> writer(os);
364
0
        succ = converter.Convert(message, writer);
365
0
    } else {
366
0
        BUTIL_RAPIDJSON_NAMESPACE::OptimizedWriter<OutputStream> writer(os);
367
0
        succ = converter.Convert(message, writer);
368
0
    }
369
0
    if (!succ && error) {
370
0
        error->clear();
371
0
        error->append(converter.ErrorText());
372
0
    }
373
0
    return succ;
374
0
}
Unexecuted instantiation: bool json2pb::ProtoMessageToJsonStream<butil::rapidjson::GenericStringBuffer<butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator> >(google::protobuf::Message const&, json2pb::Pb2JsonOptions const&, butil::rapidjson::GenericStringBuffer<butil::rapidjson::UTF8<char>, butil::rapidjson::CrtAllocator>&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*)
Unexecuted instantiation: bool json2pb::ProtoMessageToJsonStream<json2pb::ZeroCopyStreamWriter>(google::protobuf::Message const&, json2pb::Pb2JsonOptions const&, json2pb::ZeroCopyStreamWriter&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*)
375
376
bool ProtoMessageToJson(const google::protobuf::Message& message,
377
                        std::string* json,
378
                        const Pb2JsonOptions& options,
379
0
                        std::string* error) {
380
    // TODO(gejun): We could further wrap a std::string as a buffer to reduce
381
    // a copying.
382
0
    BUTIL_RAPIDJSON_NAMESPACE::StringBuffer buffer;
383
0
    if (json2pb::ProtoMessageToJsonStream(message, options, buffer, error)) {
384
0
        json->append(buffer.GetString(), buffer.GetSize());
385
0
        return true;
386
0
    }
387
0
    return false;
388
0
}
389
390
bool ProtoMessageToJson(const google::protobuf::Message& message,
391
0
                        std::string* json, std::string* error) {
392
0
    return ProtoMessageToJson(message, json, Pb2JsonOptions(), error);
393
0
}
394
395
bool ProtoMessageToJson(const google::protobuf::Message& message,
396
                        google::protobuf::io::ZeroCopyOutputStream* stream,
397
0
                        const Pb2JsonOptions& options, std::string* error) {
398
0
    json2pb::ZeroCopyStreamWriter wrapper(stream);
399
0
    return json2pb::ProtoMessageToJsonStream(message, options, wrapper, error);
400
0
}
401
402
bool ProtoMessageToJson(const google::protobuf::Message& message,
403
                        google::protobuf::io::ZeroCopyOutputStream* stream,
404
0
                        std::string* error) {
405
0
    return ProtoMessageToJson(message, stream, Pb2JsonOptions(), error);
406
0
}
407
408
bool ProtoMessageToProtoJson(const google::protobuf::Message& message,
409
                             google::protobuf::io::ZeroCopyOutputStream* json,
410
0
                             const Pb2ProtoJsonOptions& options, std::string* error) {
411
0
    if (ExceedMaxDepth(message, 0)) {
412
0
        if (error) {
413
0
            *error = "Exceeded maximum recursion depth";
414
0
        }
415
0
        return false;
416
0
    }
417
0
    butil::IOBuf buf;
418
0
    butil::IOBufAsZeroCopyOutputStream output_stream(&buf);
419
0
    if (!message.SerializeToZeroCopyStream(&output_stream)) {
420
0
        return false;
421
0
    }
422
423
0
    TypeResolverUniqueptr type_resolver = GetTypeResolver(message);
424
0
    butil::IOBufAsZeroCopyInputStream input_stream(buf);
425
0
    auto st = google::protobuf::util::BinaryToJsonStream(
426
0
            type_resolver.get(), GetTypeUrl(message), &input_stream, json, options);
427
428
0
    bool ok = st.ok();
429
0
    if (!ok && NULL != error) {
430
0
        *error = st.ToString();
431
0
    }
432
0
    return ok;
433
0
}
434
435
bool ProtoMessageToProtoJson(const google::protobuf::Message& message, std::string* json,
436
0
                             const Pb2ProtoJsonOptions& options, std::string* error) {
437
0
    google::protobuf::io::StringOutputStream output_stream(json);
438
0
    return ProtoMessageToProtoJson(message, &output_stream, options, error);
439
0
}
440
441
} // namespace json2pb