diff --git a/ruby/ext/google/protobuf_c/defs.c b/ruby/ext/google/protobuf_c/defs.c index 1d912c1a2e1..992f54bc4d6 100644 --- a/ruby/ext/google/protobuf_c/defs.c +++ b/ruby/ext/google/protobuf_c/defs.c @@ -868,6 +868,20 @@ static VALUE FieldDescriptor_default(VALUE _self) { return Convert_UpbToRuby(default_val, TypeInfo_get(self->fielddef), Qnil); } + +/* + * call-seq: + * FieldDescriptor.json_name => json_name + * + * Returns this field's json_name, as a Ruby string, or nil if not yet set. + */ +static VALUE FieldDescriptor_json_name(VALUE _self) { + FieldDescriptor* self = ruby_to_FieldDescriptor(_self); + const upb_fielddef *f = self->fielddef; + const char *json_name = upb_fielddef_jsonname(f); + return rb_str_new2(json_name); +} + /* * call-seq: * FieldDescriptor.label => label @@ -1043,6 +1057,7 @@ static void FieldDescriptor_register(VALUE module) { rb_define_method(klass, "name", FieldDescriptor_name, 0); rb_define_method(klass, "type", FieldDescriptor__type, 0); rb_define_method(klass, "default", FieldDescriptor_default, 0); + rb_define_method(klass, "json_name", FieldDescriptor_json_name, 0); rb_define_method(klass, "label", FieldDescriptor_label, 0); rb_define_method(klass, "number", FieldDescriptor_number, 0); rb_define_method(klass, "submsg_name", FieldDescriptor_submsg_name, 0); @@ -1750,6 +1765,16 @@ static void msgdef_add_field(VALUE msgbuilder_rb, upb_label_t label, VALUE name, field_proto, FileBuilderContext_strdup(self->file_builder, default_value)); } + + if (rb_funcall(options, rb_intern("key?"), 1, + ID2SYM(rb_intern("json_name"))) == Qtrue) { + VALUE json_name = + rb_hash_lookup(options, ID2SYM(rb_intern("json_name"))); + + google_protobuf_FieldDescriptorProto_set_json_name( + field_proto, + FileBuilderContext_strdup(self->file_builder, json_name)); + } } if (oneof_index >= 0) { @@ -1899,18 +1924,20 @@ static VALUE MessageBuilderContext_required(int argc, VALUE* argv, */ static VALUE MessageBuilderContext_repeated(int argc, VALUE* argv, VALUE _self) { - VALUE name, type, number, type_class; + VALUE name, type, number; + VALUE type_class, options = Qnil; - if (argc < 3) { - rb_raise(rb_eArgError, "Expected at least 3 arguments."); + rb_scan_args(argc, argv, "32", &name, &type, &number, &type_class, &options); + + // Allow passing (name, type, number, options) or + // (name, type, number, type_class, options) + if (argc == 4 && RB_TYPE_P(type_class, T_HASH)) { + options = type_class; + type_class = Qnil; } - name = argv[0]; - type = argv[1]; - number = argv[2]; - type_class = (argc > 3) ? argv[3] : Qnil; msgdef_add_field(_self, UPB_LABEL_REPEATED, name, type, number, type_class, - Qnil, -1, false); + options, -1, false); return Qnil; } diff --git a/ruby/tests/encode_decode_test.rb b/ruby/tests/encode_decode_test.rb index cce364d0d81..429ac433221 100755 --- a/ruby/tests/encode_decode_test.rb +++ b/ruby/tests/encode_decode_test.rb @@ -95,4 +95,10 @@ def test_encode_wrong_msg end end + def test_json_name + msg = A::B::C::TestJsonName.new(:value => 42) + json = msg.to_json + assert_match json, "{\"CustomJsonName\":42}" + end + end diff --git a/ruby/tests/generated_code.proto b/ruby/tests/generated_code.proto index e8116697d7a..bfdfa5aa780 100644 --- a/ruby/tests/generated_code.proto +++ b/ruby/tests/generated_code.proto @@ -83,3 +83,7 @@ message TestUnknown { map map_unknown = 67; int32 unknown_field = 89; } + +message TestJsonName { + int32 value = 1 [json_name = "CustomJsonName"]; +} diff --git a/src/google/protobuf/compiler/ruby/ruby_generator.cc b/src/google/protobuf/compiler/ruby/ruby_generator.cc index a6935c76584..cca69de0872 100644 --- a/src/google/protobuf/compiler/ruby/ruby_generator.cc +++ b/src/google/protobuf/compiler/ruby/ruby_generator.cc @@ -220,6 +220,11 @@ void GenerateField(const FieldDescriptor* field, io::Printer* printer) { DefaultValueForField(field)); } + if (field->has_json_name()) { + printer->Print(", json_name: \"$json_name$\"", "json_name", + field->json_name()); + } + printer->Print("\n"); } }