Coverage Report

Created: 2026-06-22 07:03

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