Cherry-pick #8356 into 3.16.x (#8518)
* Ruby: Add support for proto3 json_name in compiler and field definitions
* Address review feedback
* Add test for json_name functionality
Co-authored-by: Lukas Fittl <lukas@fittl.com>
diff --git a/ruby/ext/google/protobuf_c/defs.c b/ruby/ext/google/protobuf_c/defs.c
index 1d912c1..992f54b 100644
--- a/ruby/ext/google/protobuf_c/defs.c
+++ b/ruby/ext/google/protobuf_c/defs.c
@@ -868,6 +868,20 @@
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 @@
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 @@
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_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 cce364d..429ac43 100755
--- a/ruby/tests/encode_decode_test.rb
+++ b/ruby/tests/encode_decode_test.rb
@@ -95,4 +95,10 @@
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 e811669..bfdfa5a 100644
--- a/ruby/tests/generated_code.proto
+++ b/ruby/tests/generated_code.proto
@@ -83,3 +83,7 @@
map<string, TestUnknown> 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 a6935c7..cca69de 100644
--- a/src/google/protobuf/compiler/ruby/ruby_generator.cc
+++ b/src/google/protobuf/compiler/ruby/ruby_generator.cc
@@ -220,6 +220,11 @@
DefaultValueForField(field));
}
+ if (field->has_json_name()) {
+ printer->Print(", json_name: \"$json_name$\"", "json_name",
+ field->json_name());
+ }
+
printer->Print("\n");
}
}