Как я могу добавить свой собственный код в сгенерированные JAVA классы из файла proto?

Я использую protobuf и создаю классы JAVA из следующего файла proto.

syntax = "proto3";
enum Greeting {
    NONE = 0;
    MR = 1;
    MRS = 2;
    MISS = 3;
}

message Hello {
    Greeting greeting = 1;
    string name = 2;
}

message Bye {
    string name = 1;
}

option java_multiple_files = true;

Теперь мне нужно добавить код к сгенерированным файлам, и я обнаружил, что это возможно с помощью пользовательского плагина ( https://developers.google.com/protocol-buffers/docs/reference/java-generated). Я пытаюсь создать этот плагин в Java, что-то вроде этого.

public class Test {
   PluginProtos.CodeGeneratorResponse.getDefaultInstance();
   /* Code to get generated files from java_out and use the insertion points */
   codeGeneratorResponse.writeTo(System.out);
}

А потом бегаю

protoc --java_out=./classes --plugin=protoc-gen-demo=my-plugin --demo_out=. example.proto

Проблема в том, что на моем Test.java Основной метод, я не знаю, как получить доступ к файлам, созданным опцией --java_out так что я могу использовать их точки вставки. В настоящее время CodeGeneratorResponse для экземпляра по умолчанию пуст (нет файлов).

Кто-нибудь знает, как я могу получить CodeGeneratorResponse из --java_out, чтобы я мог добавить больше кода к сгенерированным классам?

Заранее спасибо.

3 ответа

Недавно я тоже боролся с этим и не смог найти хорошего ответа. Я наконец понял это после того, как некоторое время смотрел на комментарии в сообщении CodeGeneratorResponse.

Поначалу меня оттолкнуло то, что я думал о плагинах как о конвейере, в котором выходные данные одного передаются в следующий. Однако каждый плагин получает одинаковые входные данные (проанализированные файлы.proto, выраженные черезCodeGeneratorRequestmessages), и весь сгенерированный код из плагинов (включая встроенные) объединяется в выходной файл. Однако плагины могут изменять вывод предыдущих плагинов, для чего предназначены точки вставки.

Конкретно к своему вопросу вы должны добавить file на ответ с name поле, устанавливаемое на имя сгенерированного файла Java, insertion_point в поле устанавливается имя точки вставки, в которую вы хотите добавить код, а поле content в поле устанавливается код, который вы хотите вставить в этот момент.

Я нашел эту статью полезной при создании простого плагина (в данном случае на Python). В качестве простого теста я изменилgenerate_code функции из этой статьи, чтобы она выглядела так:

def generate_code(request, response):
    for proto_file in request.proto_file:
        f = response.file.add()
        f.name = "Test.java"
        f.insertion_point = "outer_class_scope"
        f.content = "// Inserting a comment as a test"

Затем я запустил протокол с плагином:

$ cat test.proto
syntax = "proto3";
message MyMsg {
    int32 num = 1;
}
$ protoc --plugin=protoc-gen-sample=sample_proto_gen.py --java_out=. --sample_out=. test.proto
$ tail -n3 Test.java
  // Inserting a comment as a test
  // @@protoc_insertion_point(outer_class_scope)
}

Ваш плагин просто должен быть исполняемым файлом, который читает CodeGeneratorRequest сообщение от стандартного ввода и пишет CodeGeneratorResponseсообщение в стандартный вывод, поэтому его можно было бы написать на Java. Я просто выбрал python, так как в целом мне он удобнее, и нашел этот простой пример.

Я создал собственный плагин для Python. Чтобы запустить мой плагин, я использую следующую команду:

protoc --plugin=protoc-gen-custom=my_plugin_executable_file --custom_out=./build test.proto

Итак, я думаю, что вам нужно сгенерировать исполняемый файл из вашего.java файла и использовать его в своей команде.

Непонятно, какой тип Java-кода вы хотите добавить,
но согласно документации protobuf , сообщения являются лишь неизменяемыми держателями данных,

Если вы хотите добавить более богатое поведение в сгенерированный класс, лучший способ сделать это — обернуть сгенерированный класс буфера протокола в класс, специфичный для приложения.

То есть создайте класс-декоратор с экземпляром сообщения и добавьте туда свой собственный Java-код, используя MyMessage.Builder в конечном итоге для создания нового экземпляра измененного сообщения.

      public class MyMessageDecorator {

    private MyMessage message;
    
    public MyMessageDecorator(MyMessage message) {
        super();
        this.mmessage=messsage;
    }

    //access to the message
    public MyMessage getMessage() {
        return message;
    }

    //add behaviour
    public <result> addBehaviour(<params>) {
        //my java code here
        ...
    }

}

Другие вопросы по тегам