2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
19 * Contains some contributions under the Thrift Software License.
20 * Please see doc/old-thrift-license.txt in the Thrift distribution for
33 #include "thrift/platform.h"
34 #include "thrift/generate/t_generator.h"
39 using std::ostringstream
;
41 using std::stringstream
;
45 static const string endl
= "\n";
46 static const string quot
= "\"";
47 static const bool NO_INDENT
= false;
48 static const bool FORCE_STRING
= true;
50 class t_json_generator
: public t_generator
{
52 t_json_generator(t_program
* program
,
53 const std::map
<std::string
, std::string
>& parsed_options
,
54 const std::string
& option_string
)
55 : t_generator(program
) {
57 std::map
<std::string
, std::string
>::const_iterator iter
;
59 should_merge_includes_
= false;
60 for( iter
= parsed_options
.begin(); iter
!= parsed_options
.end(); ++iter
) {
61 if( iter
->first
.compare("merge") == 0) {
62 should_merge_includes_
= true;
64 throw "unknown option json:" + iter
->first
;
68 out_dir_base_
= "gen-json";
71 ~t_json_generator() override
= default;
74 * Init and close methods
77 void init_generator() override
;
78 void close_generator() override
;
80 void generate_typedef(t_typedef
* ttypedef
) override
;
81 void generate_enum(t_enum
* tenum
) override
;
82 void generate_program() override
;
83 void generate_function(t_function
* tfunc
);
84 void generate_field(t_field
* field
);
86 void generate_service(t_service
* tservice
) override
;
87 void generate_struct(t_struct
* tstruct
) override
;
90 bool should_merge_includes_
;
92 ofstream_with_content_based_conditional_update f_json_
;
93 std::stack
<bool> comma_needed_
;
96 string
number_to_string(T t
) {
97 std::ostringstream out
;
98 out
.imbue(std::locale::classic());
99 out
.precision(std::numeric_limits
<T
>::digits10
);
104 template <typename T
>
105 void write_number(T n
) {
106 f_json_
<< number_to_string(n
);
109 string
get_type_name(t_type
* ttype
);
110 string
get_qualified_name(t_type
* ttype
);
112 void start_object(bool should_indent
= true);
116 void write_comma_if_needed();
117 void indicate_comma_needed();
118 string
escape_json_string(const string
& input
);
119 string
json_str(const string
& str
);
120 void merge_includes(t_program
*);
122 void generate_constant(t_const
* con
);
124 void write_type_spec_entry(const char* name
, t_type
* ttype
);
125 void write_type_spec_object(const char* name
, t_type
* ttype
);
126 void write_type_spec(t_type
* ttype
);
127 void write_string(const string
& value
);
128 void write_value(t_type
* tvalue
);
129 void write_const_value(t_const_value
* value
, bool force_string
= false);
130 void write_key_and(string key
);
131 void write_key_and_string(string key
, string val
);
132 void write_key_and_integer(string key
, int val
);
133 void write_key_and_bool(string key
, bool val
);
136 void t_json_generator::init_generator() {
137 MKDIR(get_out_dir().c_str());
139 string f_json_name
= get_out_dir() + program_
->get_name() + ".json";
140 f_json_
.open(f_json_name
.c_str());
142 // Merge all included programs into this one so we can output one big file.
143 if (should_merge_includes_
) {
144 merge_includes(program_
);
148 string
t_json_generator::escape_json_string(const string
& input
) {
149 std::ostringstream ss
;
150 for (char iter
: input
) {
184 void t_json_generator::start_object(bool should_indent
) {
185 f_json_
<< (should_indent
? indent() : "") << "{" << endl
;
187 comma_needed_
.push(false);
190 void t_json_generator::start_array() {
191 f_json_
<< "[" << endl
;
193 comma_needed_
.push(false);
196 void t_json_generator::write_comma_if_needed() {
197 if (comma_needed_
.top()) {
198 f_json_
<< "," << endl
;
202 void t_json_generator::indicate_comma_needed() {
204 comma_needed_
.push(true);
207 void t_json_generator::write_key_and(string key
) {
208 write_comma_if_needed();
209 indent(f_json_
) << json_str(key
) << ": ";
210 indicate_comma_needed();
213 void t_json_generator::write_key_and_integer(string key
, int val
) {
214 write_comma_if_needed();
215 indent(f_json_
) << json_str(key
) << ": " << number_to_string(val
);
216 indicate_comma_needed();
219 void t_json_generator::write_key_and_string(string key
, string val
) {
220 write_comma_if_needed();
221 indent(f_json_
) << json_str(key
) << ": " << json_str(val
);
222 indicate_comma_needed();
225 void t_json_generator::write_key_and_bool(string key
, bool val
) {
226 write_comma_if_needed();
227 indent(f_json_
) << json_str(key
) << ": " << (val
? "true" : "false");
228 indicate_comma_needed();
231 void t_json_generator::end_object() {
233 f_json_
<< endl
<< indent() << "}";
237 void t_json_generator::end_array() {
239 if (comma_needed_
.top()) {
242 indent(f_json_
) << "]";
246 void t_json_generator::write_type_spec_object(const char* name
, t_type
* ttype
) {
247 ttype
= ttype
->get_true_type();
248 if (ttype
->is_struct() || ttype
->is_xception() || ttype
->is_container()) {
250 start_object(NO_INDENT
);
251 write_key_and("typeId");
252 write_type_spec(ttype
);
257 void t_json_generator::write_type_spec_entry(const char* name
, t_type
* ttype
) {
259 write_type_spec(ttype
);
262 void t_json_generator::write_type_spec(t_type
* ttype
) {
263 ttype
= ttype
->get_true_type();
265 write_string(get_type_name(ttype
));
267 if (ttype
->annotations_
.size() > 0) {
268 write_key_and("annotations");
270 for (auto & annotation
: ttype
->annotations_
) {
271 write_key_and_string(annotation
.first
, annotation
.second
);
276 if (ttype
->is_struct() || ttype
->is_xception()) {
277 write_key_and_string("class", get_qualified_name(ttype
));
278 } else if (ttype
->is_map()) {
279 t_type
* ktype
= ((t_map
*)ttype
)->get_key_type();
280 t_type
* vtype
= ((t_map
*)ttype
)->get_val_type();
281 write_key_and_string("keyTypeId", get_type_name(ktype
));
282 write_key_and_string("valueTypeId", get_type_name(vtype
));
283 write_type_spec_object("keyType", ktype
);
284 write_type_spec_object("valueType", vtype
);
285 } else if (ttype
->is_list()) {
286 t_type
* etype
= ((t_list
*)ttype
)->get_elem_type();
287 write_key_and_string("elemTypeId", get_type_name(etype
));
288 write_type_spec_object("elemType", etype
);
289 } else if (ttype
->is_set()) {
290 t_type
* etype
= ((t_set
*)ttype
)->get_elem_type();
291 write_key_and_string("elemTypeId", get_type_name(etype
));
292 write_type_spec_object("elemType", etype
);
296 void t_json_generator::close_generator() {
301 void t_json_generator::merge_includes(t_program
* program
) {
302 vector
<t_program
*> includes
= program
->get_includes();
303 vector
<t_program
*>::iterator inc_iter
;
304 for (inc_iter
= includes
.begin(); inc_iter
!= includes
.end(); ++inc_iter
) {
305 t_program
* include
= *inc_iter
;
306 // recurse in case we get crazy
307 merge_includes(include
);
309 vector
<t_enum
*> enums
= include
->get_enums();
310 vector
<t_enum
*>::iterator en_iter
;
311 for (en_iter
= enums
.begin(); en_iter
!= enums
.end(); ++en_iter
) {
312 program
->add_enum(*en_iter
);
315 vector
<t_typedef
*> typedefs
= include
->get_typedefs();
316 vector
<t_typedef
*>::iterator td_iter
;
317 for (td_iter
= typedefs
.begin(); td_iter
!= typedefs
.end(); ++td_iter
) {
318 program
->add_typedef(*td_iter
);
321 vector
<t_struct
*> objects
= include
->get_objects();
322 vector
<t_struct
*>::iterator o_iter
;
323 for (o_iter
= objects
.begin(); o_iter
!= objects
.end(); ++o_iter
) {
324 program
->add_struct(*o_iter
);
327 vector
<t_const
*> consts
= include
->get_consts();
328 vector
<t_const
*>::iterator c_iter
;
329 for (c_iter
= consts
.begin(); c_iter
!= consts
.end(); ++c_iter
) {
330 program
->add_const(*c_iter
);
334 vector
<t_service
*> services
= include
->get_services();
335 vector
<t_service
*>::iterator sv_iter
;
336 for (sv_iter
= services
.begin(); sv_iter
!= services
.end(); ++sv_iter
) {
337 program
->add_service(*sv_iter
);
342 void t_json_generator::generate_program() {
347 write_key_and_string("name", program_
->get_name());
348 if (program_
->has_doc()) {
349 write_key_and_string("doc", program_
->get_doc());
352 // When merging includes, the "namespaces" and "includes" sections
353 // become ambiguous, so just skip them.
354 if (!should_merge_includes_
) {
355 // Generate namespaces
356 write_key_and("namespaces");
357 start_object(NO_INDENT
);
358 const map
<string
, string
>& namespaces
= program_
->get_namespaces();
359 map
<string
, string
>::const_iterator ns_it
;
360 for (ns_it
= namespaces
.begin(); ns_it
!= namespaces
.end(); ++ns_it
) {
361 write_key_and_string(ns_it
->first
, ns_it
->second
);
362 indicate_comma_needed();
367 write_key_and("includes");
369 const vector
<t_program
*> includes
= program_
->get_includes();
370 vector
<t_program
*>::const_iterator inc_it
;
371 for (inc_it
= includes
.begin(); inc_it
!= includes
.end(); ++inc_it
) {
372 write_comma_if_needed();
373 write_string((*inc_it
)->get_name());
374 indicate_comma_needed();
380 write_key_and("enums");
382 vector
<t_enum
*> enums
= program_
->get_enums();
383 vector
<t_enum
*>::iterator en_iter
;
384 for (en_iter
= enums
.begin(); en_iter
!= enums
.end(); ++en_iter
) {
385 write_comma_if_needed();
386 generate_enum(*en_iter
);
387 indicate_comma_needed();
392 write_key_and("typedefs");
394 vector
<t_typedef
*> typedefs
= program_
->get_typedefs();
395 vector
<t_typedef
*>::iterator td_iter
;
396 for (td_iter
= typedefs
.begin(); td_iter
!= typedefs
.end(); ++td_iter
) {
397 write_comma_if_needed();
398 generate_typedef(*td_iter
);
399 indicate_comma_needed();
403 // Generate structs, exceptions, and unions in declared order
404 write_key_and("structs");
406 vector
<t_struct
*> objects
= program_
->get_objects();
407 vector
<t_struct
*>::iterator o_iter
;
408 for (o_iter
= objects
.begin(); o_iter
!= objects
.end(); ++o_iter
) {
409 write_comma_if_needed();
410 if ((*o_iter
)->is_xception()) {
411 generate_xception(*o_iter
);
413 generate_struct(*o_iter
);
415 indicate_comma_needed();
419 // Generate constants
420 write_key_and("constants");
422 vector
<t_const
*> consts
= program_
->get_consts();
423 vector
<t_const
*>::iterator c_iter
;
424 for (c_iter
= consts
.begin(); c_iter
!= consts
.end(); ++c_iter
) {
425 write_comma_if_needed();
426 generate_constant(*c_iter
);
427 indicate_comma_needed();
432 write_key_and("services");
434 vector
<t_service
*> services
= program_
->get_services();
435 vector
<t_service
*>::iterator sv_iter
;
436 for (sv_iter
= services
.begin(); sv_iter
!= services
.end(); ++sv_iter
) {
437 write_comma_if_needed();
438 generate_service(*sv_iter
);
439 indicate_comma_needed();
445 // Close the generator
449 void t_json_generator::generate_typedef(t_typedef
* ttypedef
) {
451 write_key_and_string("name", get_qualified_name(ttypedef
));
452 write_key_and_string("typeId", get_type_name(ttypedef
->get_true_type()));
453 write_type_spec_object("type", ttypedef
->get_true_type());
454 if (ttypedef
->has_doc()) {
455 write_key_and_string("doc", ttypedef
->get_doc());
457 if (ttypedef
->annotations_
.size() > 0) {
458 write_key_and("annotations");
460 for (auto & annotation
: ttypedef
->annotations_
) {
461 write_key_and_string(annotation
.first
, annotation
.second
);
468 void t_json_generator::write_string(const string
& value
) {
469 f_json_
<< quot
<< escape_json_string(value
) << quot
;
472 void t_json_generator::write_const_value(t_const_value
* value
, bool should_force_string
) {
474 switch (value
->get_type()) {
476 case t_const_value::CV_IDENTIFIER
:
477 case t_const_value::CV_INTEGER
:
478 if (should_force_string
) {
479 write_string(number_to_string(value
->get_integer()));
481 write_number(value
->get_integer());
485 case t_const_value::CV_DOUBLE
:
486 if (should_force_string
) {
487 write_string(number_to_string(value
->get_double()));
489 write_number(value
->get_double());
493 case t_const_value::CV_STRING
:
494 write_string(value
->get_string());
497 case t_const_value::CV_LIST
: {
499 std::vector
<t_const_value
*> list
= value
->get_list();
500 std::vector
<t_const_value
*>::iterator lit
;
501 for (lit
= list
.begin(); lit
!= list
.end(); ++lit
) {
502 write_comma_if_needed();
504 write_const_value(*lit
);
505 indicate_comma_needed();
511 case t_const_value::CV_MAP
: {
512 start_object(NO_INDENT
);
513 std::map
<t_const_value
*, t_const_value
*, t_const_value::value_compare
> map
= value
->get_map();
514 std::map
<t_const_value
*, t_const_value
*, t_const_value::value_compare
>::iterator mit
;
515 for (mit
= map
.begin(); mit
!= map
.end(); ++mit
) {
516 write_comma_if_needed();
518 // JSON objects only allow string keys
519 write_const_value(mit
->first
, FORCE_STRING
);
521 write_const_value(mit
->second
);
522 indicate_comma_needed();
534 string
t_json_generator::json_str(const string
& str
) {
535 return quot
+ escape_json_string(str
) + quot
;
538 void t_json_generator::generate_constant(t_const
* con
) {
541 write_key_and_string("name", con
->get_name());
542 write_key_and_string("typeId", get_type_name(con
->get_type()));
543 write_type_spec_object("type", con
->get_type());
545 if (con
->has_doc()) {
546 write_key_and_string("doc", con
->get_doc());
549 write_key_and("value");
550 write_const_value(con
->get_value());
555 void t_json_generator::generate_enum(t_enum
* tenum
) {
558 write_key_and_string("name", tenum
->get_name());
560 if (tenum
->has_doc()) {
561 write_key_and_string("doc", tenum
->get_doc());
564 if (tenum
->annotations_
.size() > 0) {
565 write_key_and("annotations");
567 for (auto & annotation
: tenum
->annotations_
) {
568 write_key_and_string(annotation
.first
, annotation
.second
);
573 write_key_and("members");
575 vector
<t_enum_value
*> values
= tenum
->get_constants();
576 vector
<t_enum_value
*>::iterator val_iter
;
577 for (val_iter
= values
.begin(); val_iter
!= values
.end(); ++val_iter
) {
578 write_comma_if_needed();
579 t_enum_value
* val
= (*val_iter
);
581 write_key_and_string("name", val
->get_name());
582 write_key_and_integer("value", val
->get_value());
583 if (val
->has_doc()) {
584 write_key_and_string("doc", val
->get_doc());
587 indicate_comma_needed();
594 void t_json_generator::generate_struct(t_struct
* tstruct
) {
597 write_key_and_string("name", tstruct
->get_name());
599 if (tstruct
->has_doc()) {
600 write_key_and_string("doc", tstruct
->get_doc());
603 if (tstruct
->annotations_
.size() > 0) {
604 write_key_and("annotations");
606 for (auto & annotation
: tstruct
->annotations_
) {
607 write_key_and_string(annotation
.first
, annotation
.second
);
612 write_key_and_bool("isException", tstruct
->is_xception());
614 write_key_and_bool("isUnion", tstruct
->is_union());
616 write_key_and("fields");
618 vector
<t_field
*> members
= tstruct
->get_members();
619 vector
<t_field
*>::iterator mem_iter
;
620 for (mem_iter
= members
.begin(); mem_iter
!= members
.end(); mem_iter
++) {
621 write_comma_if_needed();
622 generate_field(*mem_iter
);
623 indicate_comma_needed();
630 void t_json_generator::generate_service(t_service
* tservice
) {
633 write_key_and_string("name", get_qualified_name(tservice
));
635 if (tservice
->get_extends()) {
636 write_key_and_string("extends", get_qualified_name(tservice
->get_extends()));
639 if (tservice
->has_doc()) {
640 write_key_and_string("doc", tservice
->get_doc());
643 if (tservice
->annotations_
.size() > 0) {
644 write_key_and("annotations");
646 for (auto & annotation
: tservice
->annotations_
) {
647 write_key_and_string(annotation
.first
, annotation
.second
);
652 write_key_and("functions");
654 vector
<t_function
*> functions
= tservice
->get_functions();
655 vector
<t_function
*>::iterator fn_iter
= functions
.begin();
656 for (; fn_iter
!= functions
.end(); fn_iter
++) {
657 write_comma_if_needed();
658 generate_function(*fn_iter
);
659 indicate_comma_needed();
666 void t_json_generator::generate_function(t_function
* tfunc
) {
669 write_key_and_string("name", tfunc
->get_name());
671 write_key_and_string("returnTypeId", get_type_name(tfunc
->get_returntype()));
672 write_type_spec_object("returnType", tfunc
->get_returntype());
674 write_key_and_bool("oneway", tfunc
->is_oneway());
676 if (tfunc
->has_doc()) {
677 write_key_and_string("doc", tfunc
->get_doc());
680 if (tfunc
->annotations_
.size() > 0) {
681 write_key_and("annotations");
683 for (auto & annotation
: tfunc
->annotations_
) {
684 write_key_and_string(annotation
.first
, annotation
.second
);
689 write_key_and("arguments");
691 vector
<t_field
*> members
= tfunc
->get_arglist()->get_members();
692 vector
<t_field
*>::iterator mem_iter
= members
.begin();
693 for (; mem_iter
!= members
.end(); mem_iter
++) {
694 write_comma_if_needed();
695 generate_field(*mem_iter
);
696 indicate_comma_needed();
700 write_key_and("exceptions");
702 vector
<t_field
*> excepts
= tfunc
->get_xceptions()->get_members();
703 vector
<t_field
*>::iterator ex_iter
= excepts
.begin();
704 for (; ex_iter
!= excepts
.end(); ex_iter
++) {
705 write_comma_if_needed();
706 generate_field(*ex_iter
);
707 indicate_comma_needed();
714 void t_json_generator::generate_field(t_field
* field
) {
717 write_key_and_integer("key", field
->get_key());
718 write_key_and_string("name", field
->get_name());
719 write_key_and_string("typeId", get_type_name(field
->get_type()));
720 write_type_spec_object("type", field
->get_type());
722 if (field
->has_doc()) {
723 write_key_and_string("doc", field
->get_doc());
726 if (field
->annotations_
.size() > 0) {
727 write_key_and("annotations");
729 for (auto & annotation
: field
->annotations_
) {
730 write_key_and_string(annotation
.first
, annotation
.second
);
735 write_key_and("required");
736 switch (field
->get_req()) {
737 case t_field::T_REQUIRED
:
738 write_string("required");
740 case t_field::T_OPT_IN_REQ_OUT
:
741 write_string("req_out");
744 write_string("optional");
748 if (field
->get_value()) {
749 write_key_and("default");
750 write_const_value(field
->get_value());
756 string
t_json_generator::get_type_name(t_type
* ttype
) {
757 ttype
= ttype
->get_true_type();
758 if (ttype
->is_list()) {
761 if (ttype
->is_set()) {
764 if (ttype
->is_map()) {
767 if (ttype
->is_enum()) {
770 if (ttype
->is_struct()) {
771 return ((t_struct
*)ttype
)->is_union() ? "union" : "struct";
773 if (ttype
->is_xception()) {
776 if (ttype
->is_base_type()) {
777 t_base_type
* tbasetype
= (t_base_type
*)ttype
;
778 return tbasetype
->is_binary() ? "binary" : t_base_type::t_base_name(tbasetype
->get_base());
784 string
t_json_generator::get_qualified_name(t_type
* ttype
) {
785 if (should_merge_includes_
|| ttype
->get_program() == program_
) {
786 return ttype
->get_name();
788 return ttype
->get_program()->get_name() + "." + ttype
->get_name();
791 THRIFT_REGISTER_GENERATOR(json
,
793 " merge: Generate output with included files merged\n")