grpc: protobuf 跨语言代码生成导致命名不一致

grpc: protobuf cross-language code generation results in naming inconsistency

我发现在 protobuf 定义中使用 snake_case 在不同语言中生成的 method/class 名称会略有不同。如果协议字段名称使用 snake_case.

,区别在于大小写

例子

基于以下协议

的常规protoc代码生成
syntax = "proto3";

package myservice;

service Myservice {
    rpc MyService(Request) returns (Reply) {}
}

message Request {
  bool my_foo = 1;
  bool my_bar = 2;
}

message Reply {
    bool is_succeeded = 1;
}

产生以下生成的命名

Python

python -m grpc_tools.protoc -I="${SRC_DIR}" --python_out="${DST_DIR}" --grpc_python_out=$DST_DIR --proto_path="${SRC_DIR}" my.proto

my_pb2_grpc.py

# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
"""Client and server classes corresponding to protobuf-defined services."""
import grpc

import my_pb2 as my__pb2


class MyserviceStub(object):
    """Missing associated documentation comment in .proto file."""

    def __init__(self, channel):
        """Constructor.

        Args:
            channel: A grpc.Channel.
        """
        self.MyService = channel.unary_unary(
                '/myservice.Myservice/MyService',
                request_serializer=my__pb2.Request.SerializeToString,
                response_deserializer=my__pb2.Reply.FromString,
                )


class MyserviceServicer(object):
    """Missing associated documentation comment in .proto file."""

    def MyService(self, request, context):
        """Missing associated documentation comment in .proto file."""
        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
        context.set_details('Method not implemented!')
        raise NotImplementedError('Method not implemented!')


def add_MyserviceServicer_to_server(servicer, server):
    rpc_method_handlers = {
            'MyService': grpc.unary_unary_rpc_method_handler(
                    servicer.MyService,
                    request_deserializer=my__pb2.Request.FromString,
                    response_serializer=my__pb2.Reply.SerializeToString,
            ),
    }
    generic_handler = grpc.method_handlers_generic_handler(
            'myservice.Myservice', rpc_method_handlers)
    server.add_generic_rpc_handlers((generic_handler,))


 # This class is part of an EXPERIMENTAL API.
class Myservice(object):
    """Missing associated documentation comment in .proto file."""

    @staticmethod
    def MyService(request,
            target,
            options=(),
            channel_credentials=None,
            call_credentials=None,
            insecure=False,
            compression=None,
            wait_for_ready=None,
            timeout=None,
            metadata=None):
        return grpc.experimental.unary_unary(request, target, '/myservice.Myservice/MyService',
            my__pb2.Request.SerializeToString,
            my__pb2.Reply.FromString,
            options, channel_credentials,
            insecure, call_credentials, compression, wait_for_ready, timeout, metadata)


my_pb2.py

# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler.  DO NOT EDIT!
# source: my.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)

_sym_db = _symbol_database.Default()




DESCRIPTOR = _descriptor.FileDescriptor(
  name='my.proto',
  package='myservice',
  syntax='proto3',
  serialized_options=None,
  create_key=_descriptor._internal_create_key,
  serialized_pb=b'\n\x08my.proto\x12\tmyservice\")\n\x07Request\x12\x0e\n\x06my_foo\x18\x01 \x01(\x08\x12\x0e\n\x06my_bar\x18\x02 \x01(\x08\"\x1d\n\x05Reply\x12\x14\n\x0cis_succeeded\x18\x01 \x01(\x08\x32@\n\tMyservice\x12\x33\n\tMyService\x12\x12.myservice.Request\x1a\x10.myservice.Reply\"\x00\x62\x06proto3'
)




_REQUEST = _descriptor.Descriptor(
  name='Request',
  full_name='myservice.Request',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  create_key=_descriptor._internal_create_key,
  fields=[
    _descriptor.FieldDescriptor(
      name='my_foo', full_name='myservice.Request.my_foo', index=0,
      number=1, type=8, cpp_type=7, label=1,
      has_default_value=False, default_value=False,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
    _descriptor.FieldDescriptor(
      name='my_bar', full_name='myservice.Request.my_bar', index=1,
      number=2, type=8, cpp_type=7, label=1,
      has_default_value=False, default_value=False,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
  ],
  extensions=[
  ],
  nested_types=[],
  enum_types=[
  ],
  serialized_options=None,
  is_extendable=False,
  syntax='proto3',
  extension_ranges=[],
  oneofs=[
  ],
  serialized_start=23,
  serialized_end=64,
)


_REPLY = _descriptor.Descriptor(
  name='Reply',
  full_name='myservice.Reply',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  create_key=_descriptor._internal_create_key,
  fields=[
    _descriptor.FieldDescriptor(
      name='is_succeeded', full_name='myservice.Reply.is_succeeded', index=0,
      number=1, type=8, cpp_type=7, label=1,
      has_default_value=False, default_value=False,
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      serialized_options=None, file=DESCRIPTOR,  create_key=_descriptor._internal_create_key),
  ],
  extensions=[
  ],
  nested_types=[],
  enum_types=[
  ],
  serialized_options=None,
  is_extendable=False,
  syntax='proto3',
  extension_ranges=[],
  oneofs=[
  ],
  serialized_start=66,
  serialized_end=95,
)

DESCRIPTOR.message_types_by_name['Request'] = _REQUEST
DESCRIPTOR.message_types_by_name['Reply'] = _REPLY
_sym_db.RegisterFileDescriptor(DESCRIPTOR)

Request = _reflection.GeneratedProtocolMessageType('Request', (_message.Message,), {
  'DESCRIPTOR' : _REQUEST,
  '__module__' : 'my_pb2'
  # @@protoc_insertion_point(class_scope:myservice.Request)
  })
_sym_db.RegisterMessage(Request)

Reply = _reflection.GeneratedProtocolMessageType('Reply', (_message.Message,), {
  'DESCRIPTOR' : _REPLY,
  '__module__' : 'my_pb2'
  # @@protoc_insertion_point(class_scope:myservice.Reply)
  })
_sym_db.RegisterMessage(Reply)



_MYSERVICE = _descriptor.ServiceDescriptor(
  name='Myservice',
  full_name='myservice.Myservice',
  file=DESCRIPTOR,
  index=0,
  serialized_options=None,
  create_key=_descriptor._internal_create_key,
  serialized_start=97,
  serialized_end=161,
  methods=[
  _descriptor.MethodDescriptor(
    name='MyService',
    full_name='myservice.Myservice.MyService',
    index=0,
    containing_service=None,
    input_type=_REQUEST,
    output_type=_REPLY,
    serialized_options=None,
    create_key=_descriptor._internal_create_key,
  ),
])
_sym_db.RegisterServiceDescriptor(_MYSERVICE)

DESCRIPTOR.services_by_name['Myservice'] = _MYSERVICE

# @@protoc_insertion_point(module_scope)

Node.js

使用 grpc-node 结果如下所示

grpc_tools_node_protoc --js_out=import_style=commonjs,binary:"${DST_DIR}" --grpc_out=grpc_js:"${DST_DIR}" --proto_path="${SRC_DIR}" my.proto

my_grpc_pb.js

// GENERATED CODE -- DO NOT EDIT!

'use strict';
var grpc = require('@grpc/grpc-js');
var my_pb = require('./my_pb.js');

function serialize_myservice_Reply(arg) {
  if (!(arg instanceof my_pb.Reply)) {
    throw new Error('Expected argument of type myservice.Reply');
  }
  return Buffer.from(arg.serializeBinary());
}

function deserialize_myservice_Reply(buffer_arg) {
  return my_pb.Reply.deserializeBinary(new Uint8Array(buffer_arg));
}

function serialize_myservice_Request(arg) {
  if (!(arg instanceof my_pb.Request)) {
    throw new Error('Expected argument of type myservice.Request');
  }
  return Buffer.from(arg.serializeBinary());
}

function deserialize_myservice_Request(buffer_arg) {
  return my_pb.Request.deserializeBinary(new Uint8Array(buffer_arg));
}


var MyserviceService = exports.MyserviceService = {
  myService: {
    path: '/myservice.Myservice/MyService',
    requestStream: false,
    responseStream: false,
    requestType: my_pb.Request,
    responseType: my_pb.Reply,
    requestSerialize: serialize_myservice_Request,
    requestDeserialize: deserialize_myservice_Request,
    responseSerialize: serialize_myservice_Reply,
    responseDeserialize: deserialize_myservice_Reply,
  },
};

exports.MyserviceClient = grpc.makeGenericClientConstructor(MyserviceService);

my_pb.js

// source: my.proto
/**
 * @fileoverview
 * @enhanceable
 * @suppress {missingRequire} reports error on implicit type usages.
 * @suppress {messageConventions} JS Compiler reports an error if a variable or
 *     field starts with 'MSG_' and isn't a translatable message.
 * @public
 */
// GENERATED CODE -- DO NOT EDIT!
/* eslint-disable */
// @ts-nocheck

var jspb = require('google-protobuf');
var goog = jspb;
var global = Function('return this')();

goog.exportSymbol('proto.myservice.Reply', null, global);
goog.exportSymbol('proto.myservice.Request', null, global);
/**
 * Generated by JsPbCodeGenerator.
 * @param {Array=} opt_data Optional initial data array, typically from a
 * server response, or constructed directly in Javascript. The array is used
 * in place and becomes part of the constructed object. It is not cloned.
 * If no data is provided, the constructed object will be empty, but still
 * valid.
 * @extends {jspb.Message}
 * @constructor
 */
proto.myservice.Request = function(opt_data) {
  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.myservice.Request, jspb.Message);
if (goog.DEBUG && !COMPILED) {
  /**
   * @public
   * @override
   */
  proto.myservice.Request.displayName = 'proto.myservice.Request';
}
/**
 * Generated by JsPbCodeGenerator.
 * @param {Array=} opt_data Optional initial data array, typically from a
 * server response, or constructed directly in Javascript. The array is used
 * in place and becomes part of the constructed object. It is not cloned.
 * If no data is provided, the constructed object will be empty, but still
 * valid.
 * @extends {jspb.Message}
 * @constructor
 */
proto.myservice.Reply = function(opt_data) {
  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
};
goog.inherits(proto.myservice.Reply, jspb.Message);
if (goog.DEBUG && !COMPILED) {
  /**
   * @public
   * @override
   */
  proto.myservice.Reply.displayName = 'proto.myservice.Reply';
}



if (jspb.Message.GENERATE_TO_OBJECT) {
/**
 * Creates an object representation of this proto.
 * Field names that are reserved in JavaScript and will be renamed to pb_name.
 * Optional fields that are not set will be set to undefined.
 * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
 * For the list of reserved names please see:
 *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
 * @param {boolean=} opt_includeInstance Deprecated. whether to include the
 *     JSPB instance for transitional soy proto support:
 *     http://goto/soy-param-migration
 * @return {!Object}
 */
proto.myservice.Request.prototype.toObject = function(opt_includeInstance) {
  return proto.myservice.Request.toObject(opt_includeInstance, this);
};


/**
 * Static version of the {@see toObject} method.
 * @param {boolean|undefined} includeInstance Deprecated. Whether to include
 *     the JSPB instance for transitional soy proto support:
 *     http://goto/soy-param-migration
 * @param {!proto.myservice.Request} msg The msg instance to transform.
 * @return {!Object}
 * @suppress {unusedLocalVariables} f is only used for nested messages
 */
proto.myservice.Request.toObject = function(includeInstance, msg) {
  var f, obj = {
    myFoo: jspb.Message.getBooleanFieldWithDefault(msg, 1, false),
    myBar: jspb.Message.getBooleanFieldWithDefault(msg, 2, false)
  };

  if (includeInstance) {
    obj.$jspbMessageInstance = msg;
  }
  return obj;
};
}


/**
 * Deserializes binary data (in protobuf wire format).
 * @param {jspb.ByteSource} bytes The bytes to deserialize.
 * @return {!proto.myservice.Request}
 */
proto.myservice.Request.deserializeBinary = function(bytes) {
  var reader = new jspb.BinaryReader(bytes);
  var msg = new proto.myservice.Request;
  return proto.myservice.Request.deserializeBinaryFromReader(msg, reader);
};


/**
 * Deserializes binary data (in protobuf wire format) from the
 * given reader into the given message object.
 * @param {!proto.myservice.Request} msg The message object to deserialize into.
 * @param {!jspb.BinaryReader} reader The BinaryReader to use.
 * @return {!proto.myservice.Request}
 */
proto.myservice.Request.deserializeBinaryFromReader = function(msg, reader) {
  while (reader.nextField()) {
    if (reader.isEndGroup()) {
      break;
    }
    var field = reader.getFieldNumber();
    switch (field) {
    case 1:
      var value = /** @type {boolean} */ (reader.readBool());
      msg.setMyFoo(value);
      break;
    case 2:
      var value = /** @type {boolean} */ (reader.readBool());
      msg.setMyBar(value);
      break;
    default:
      reader.skipField();
      break;
    }
  }
  return msg;
};


/**
 * Serializes the message to binary data (in protobuf wire format).
 * @return {!Uint8Array}
 */
proto.myservice.Request.prototype.serializeBinary = function() {
  var writer = new jspb.BinaryWriter();
  proto.myservice.Request.serializeBinaryToWriter(this, writer);
  return writer.getResultBuffer();
};


/**
 * Serializes the given message to binary data (in protobuf wire
 * format), writing to the given BinaryWriter.
 * @param {!proto.myservice.Request} message
 * @param {!jspb.BinaryWriter} writer
 * @suppress {unusedLocalVariables} f is only used for nested messages
 */
proto.myservice.Request.serializeBinaryToWriter = function(message, writer) {
  var f = undefined;
  f = message.getMyFoo();
  if (f) {
    writer.writeBool(
      1,
      f
    );
  }
  f = message.getMyBar();
  if (f) {
    writer.writeBool(
      2,
      f
    );
  }
};


/**
 * optional bool my_foo = 1;
 * @return {boolean}
 */
proto.myservice.Request.prototype.getMyFoo = function() {
  return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 1, false));
};


/**
 * @param {boolean} value
 * @return {!proto.myservice.Request} returns this
 */
proto.myservice.Request.prototype.setMyFoo = function(value) {
  return jspb.Message.setProto3BooleanField(this, 1, value);
};


/**
 * optional bool my_bar = 2;
 * @return {boolean}
 */
proto.myservice.Request.prototype.getMyBar = function() {
  return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 2, false));
};


/**
 * @param {boolean} value
 * @return {!proto.myservice.Request} returns this
 */
proto.myservice.Request.prototype.setMyBar = function(value) {
  return jspb.Message.setProto3BooleanField(this, 2, value);
};





if (jspb.Message.GENERATE_TO_OBJECT) {
/**
 * Creates an object representation of this proto.
 * Field names that are reserved in JavaScript and will be renamed to pb_name.
 * Optional fields that are not set will be set to undefined.
 * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
 * For the list of reserved names please see:
 *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
 * @param {boolean=} opt_includeInstance Deprecated. whether to include the
 *     JSPB instance for transitional soy proto support:
 *     http://goto/soy-param-migration
 * @return {!Object}
 */
proto.myservice.Reply.prototype.toObject = function(opt_includeInstance) {
  return proto.myservice.Reply.toObject(opt_includeInstance, this);
};


/**
 * Static version of the {@see toObject} method.
 * @param {boolean|undefined} includeInstance Deprecated. Whether to include
 *     the JSPB instance for transitional soy proto support:
 *     http://goto/soy-param-migration
 * @param {!proto.myservice.Reply} msg The msg instance to transform.
 * @return {!Object}
 * @suppress {unusedLocalVariables} f is only used for nested messages
 */
proto.myservice.Reply.toObject = function(includeInstance, msg) {
  var f, obj = {
    isSucceeded: jspb.Message.getBooleanFieldWithDefault(msg, 1, false)
  };

  if (includeInstance) {
    obj.$jspbMessageInstance = msg;
  }
  return obj;
};
}


/**
 * Deserializes binary data (in protobuf wire format).
 * @param {jspb.ByteSource} bytes The bytes to deserialize.
 * @return {!proto.myservice.Reply}
 */
proto.myservice.Reply.deserializeBinary = function(bytes) {
  var reader = new jspb.BinaryReader(bytes);
  var msg = new proto.myservice.Reply;
  return proto.myservice.Reply.deserializeBinaryFromReader(msg, reader);
};


/**
 * Deserializes binary data (in protobuf wire format) from the
 * given reader into the given message object.
 * @param {!proto.myservice.Reply} msg The message object to deserialize into.
 * @param {!jspb.BinaryReader} reader The BinaryReader to use.
 * @return {!proto.myservice.Reply}
 */
proto.myservice.Reply.deserializeBinaryFromReader = function(msg, reader) {
  while (reader.nextField()) {
    if (reader.isEndGroup()) {
      break;
    }
    var field = reader.getFieldNumber();
    switch (field) {
    case 1:
      var value = /** @type {boolean} */ (reader.readBool());
      msg.setIsSucceeded(value);
      break;
    default:
      reader.skipField();
      break;
    }
  }
  return msg;
};


/**
 * Serializes the message to binary data (in protobuf wire format).
 * @return {!Uint8Array}
 */
proto.myservice.Reply.prototype.serializeBinary = function() {
  var writer = new jspb.BinaryWriter();
  proto.myservice.Reply.serializeBinaryToWriter(this, writer);
  return writer.getResultBuffer();
};


/**
 * Serializes the given message to binary data (in protobuf wire
 * format), writing to the given BinaryWriter.
 * @param {!proto.myservice.Reply} message
 * @param {!jspb.BinaryWriter} writer
 * @suppress {unusedLocalVariables} f is only used for nested messages
 */
proto.myservice.Reply.serializeBinaryToWriter = function(message, writer) {
  var f = undefined;
  f = message.getIsSucceeded();
  if (f) {
    writer.writeBool(
      1,
      f
    );
  }
};


/**
 * optional bool is_succeeded = 1;
 * @return {boolean}
 */
proto.myservice.Reply.prototype.getIsSucceeded = function() {
  return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 1, false));
};


/**
 * @param {boolean} value
 * @return {!proto.myservice.Reply} returns this
 */
proto.myservice.Reply.prototype.setIsSucceeded = function(value) {
  return jspb.Message.setProto3BooleanField(this, 1, value);
};


goog.object.extend(exports, proto.myservice);

飞镖

同上处理

protoc --plugin=protoc-gen-dart="$HOME/.pub-cache/bin/protoc-gen-dart" --dart_out=grpc:"${DST_DIR}" -I"${SRC_DIR}" -I"$IncludeDir" my.proto

my.pbgrpc.dart

///
//  Generated code. Do not modify.
//  source: my.proto
//
// @dart = 2.12
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields

import 'dart:async' as $async;

import 'dart:core' as $core;

import 'package:grpc/service_api.dart' as $grpc;
import 'my.pb.dart' as [=18=];
export 'my.pb.dart';

class MyserviceClient extends $grpc.Client {
  static final _$myService = $grpc.ClientMethod<[=18=].Request, [=18=].Reply>(
      '/myservice.Myservice/MyService',
      ([=18=].Request value) => value.writeToBuffer(),
      ($core.List<$core.int> value) => [=18=].Reply.fromBuffer(value));

  MyserviceClient($grpc.ClientChannel channel,
      {$grpc.CallOptions? options,
      $core.Iterable<$grpc.ClientInterceptor>? interceptors})
      : super(channel, options: options, interceptors: interceptors);

  $grpc.ResponseFuture<[=18=].Reply> myService([=18=].Request request,
      {$grpc.CallOptions? options}) {
    return $createUnaryCall(_$myService, request, options: options);
  }
}

abstract class MyserviceServiceBase extends $grpc.Service {
  $core.String get $name => 'myservice.Myservice';

  MyserviceServiceBase() {
    $addMethod($grpc.ServiceMethod<[=18=].Request, [=18=].Reply>(
        'MyService',
        myService_Pre,
        false,
        false,
        ($core.List<$core.int> value) => [=18=].Request.fromBuffer(value),
        ([=18=].Reply value) => value.writeToBuffer()));
  }

  $async.Future<[=18=].Reply> myService_Pre(
      $grpc.ServiceCall call, $async.Future<[=18=].Request> request) async {
    return myService(call, await request);
  }

  $async.Future<[=18=].Reply> myService($grpc.ServiceCall call, [=18=].Request request);
}

my.pb.dart

///
//  Generated code. Do not modify.
//  source: my.proto
//
// @dart = 2.12
// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields

import 'dart:core' as $core;

import 'package:protobuf/protobuf.dart' as $pb;

class Request extends $pb.GeneratedMessage {
  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Request', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'myservice'), createEmptyInstance: create)
    ..aOB(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'myFoo')
    ..aOB(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'myBar')
    ..hasRequiredFields = false
  ;

  Request._() : super();
  factory Request({
    $core.bool? myFoo,
    $core.bool? myBar,
  }) {
    final _result = create();
    if (myFoo != null) {
      _result.myFoo = myFoo;
    }
    if (myBar != null) {
      _result.myBar = myBar;
    }
    return _result;
  }
  factory Request.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
  factory Request.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
  @$core.Deprecated(
  'Using this can add significant overhead to your binary. '
  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
  'Will be removed in next major version')
  Request clone() => Request()..mergeFromMessage(this);
  @$core.Deprecated(
  'Using this can add significant overhead to your binary. '
  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
  'Will be removed in next major version')
  Request copyWith(void Function(Request) updates) => super.copyWith((message) => updates(message as Request)) as Request; // ignore: deprecated_member_use
  $pb.BuilderInfo get info_ => _i;
  @$core.pragma('dart2js:noInline')
  static Request create() => Request._();
  Request createEmptyInstance() => create();
  static $pb.PbList<Request> createRepeated() => $pb.PbList<Request>();
  @$core.pragma('dart2js:noInline')
  static Request getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Request>(create);
  static Request? _defaultInstance;

  @$pb.TagNumber(1)
  $core.bool get myFoo => $_getBF(0);
  @$pb.TagNumber(1)
  set myFoo($core.bool v) { $_setBool(0, v); }
  @$pb.TagNumber(1)
  $core.bool hasMyFoo() => $_has(0);
  @$pb.TagNumber(1)
  void clearMyFoo() => clearField(1);

  @$pb.TagNumber(2)
  $core.bool get myBar => $_getBF(1);
  @$pb.TagNumber(2)
  set myBar($core.bool v) { $_setBool(1, v); }
  @$pb.TagNumber(2)
  $core.bool hasMyBar() => $_has(1);
  @$pb.TagNumber(2)
  void clearMyBar() => clearField(2);
}

class Reply extends $pb.GeneratedMessage {
  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Reply', package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'myservice'), createEmptyInstance: create)
    ..aOB(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'isSucceeded')
    ..hasRequiredFields = false
  ;

  Reply._() : super();
  factory Reply({
    $core.bool? isSucceeded,
  }) {
    final _result = create();
    if (isSucceeded != null) {
      _result.isSucceeded = isSucceeded;
    }
    return _result;
  }
  factory Reply.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
  factory Reply.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
  @$core.Deprecated(
  'Using this can add significant overhead to your binary. '
  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
  'Will be removed in next major version')
  Reply clone() => Reply()..mergeFromMessage(this);
  @$core.Deprecated(
  'Using this can add significant overhead to your binary. '
  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
  'Will be removed in next major version')
  Reply copyWith(void Function(Reply) updates) => super.copyWith((message) => updates(message as Reply)) as Reply; // ignore: deprecated_member_use
  $pb.BuilderInfo get info_ => _i;
  @$core.pragma('dart2js:noInline')
  static Reply create() => Reply._();
  Reply createEmptyInstance() => create();
  static $pb.PbList<Reply> createRepeated() => $pb.PbList<Reply>();
  @$core.pragma('dart2js:noInline')
  static Reply getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Reply>(create);
  static Reply? _defaultInstance;

  @$pb.TagNumber(1)
  $core.bool get isSucceeded => $_getBF(0);
  @$pb.TagNumber(1)
  set isSucceeded($core.bool v) { $_setBool(0, v); }
  @$pb.TagNumber(1)
  $core.bool hasIsSucceeded() => $_has(0);
  @$pb.TagNumber(1)
  void clearIsSucceeded() => clearField(1);
}

比较

如果我们比较以下生成的实体

这是结果

Python

Myservice
MyService
my_foo
my_bar
is_succeeded

Node.js

MyserviceService
myService
accessor: getMyFoo()
accessor: getMyBar()
accessor: getIsSucceeded()

飞镖

MyserviceServiceBase
myService
myFoo
myBar
isSucceeded

问题

虽然这些命名方案似乎迎合了特定语言的约定, 它们之间的差异导致了一些维护问题。

我想知道是否有一种方法可以确保所有这些语言以及可能所有支持的语言都采用相同的大小写处理方式。

谢谢!

代码生成 plugins 可以完全自由地生成他们想要的任何代码。通常,他们会尝试遵循语言的约定。您需要为每种语言设置控件,但大多数不会提供。

您面临的 actual maintenance issues 是什么?也许还有其他解决方法。