Ответ 1
В настоящее время мы используем protobuf-java-format для преобразования наших сообщений Protobuf (любой подкласс Message
) в формат JSON для отправки через наш веб-API.
Просто выполните:
JsonFormat.printToString(protoMessage)
У меня есть существующая система, которая использует протокол связи protobuf между графическим интерфейсом и сервером. Теперь я хотел бы добавить некоторую настойчивость, но в настоящий момент сообщения protobuf преобразуются напрямую в сторонние пользовательские объекты.
Есть ли способ конвертировать прото-сообщения в json, которые затем могут сохраняться в базе данных.
NB: Мне не очень нравится идея создания бинарного файла protobuf для базы данных, потому что он может однажды стать не обратно совместимым с более новыми версиями и сломать систему таким образом.
В настоящее время мы используем protobuf-java-format для преобразования наших сообщений Protobuf (любой подкласс Message
) в формат JSON для отправки через наш веб-API.
Просто выполните:
JsonFormat.printToString(protoMessage)
Как упоминалось в ответе на аналогичный вопрос, поскольку v3.1.0 это поддерживаемая функция ProtocolBuffers. Для Java включите модуль расширения com.google.protobuf: protobuf-java-util и используйте JsonFormat:
JsonFormat.parser().ignoringUnknownFields().merge(json, yourObjectBuilder);
YourObject value = yourObjectBuilder.build();
Мне не очень нравится идея создания бинарного файла protobuf для базы данных, потому что он может однажды стать не обратно совместимым с более новыми версиями и сломать систему таким образом.
Преобразование protobuf в JSON для хранения, а затем обратно в protobuf при загрузке гораздо чаще создает проблемы совместимости, потому что:
При всем том есть много библиотек для преобразования протобуфов в JSON, обычно построенных на интерфейсе отражения Protobuf (чтобы не путать с интерфейсом отражения Java, отражение Protobuf предлагается интерфейсом com.google.protobuf.Message
).
Добавив ответ Ophir, JsonFormat доступен еще до версии 3.0. Однако способ сделать это немного отличается.
В Protobuf 3. 0+ класс JsonFormat является одноэлементным и поэтому выполняет что-то вроде следующего
String jsonString = "";
JsonFormat.parser().ignoringUnknownFields().merge(json,yourObjectBuilder);
В Protobuf 2. 5+ ниже должно работать
String jsonString = "";
JsonFormat jsonFormat = new JsonFormat();
jsonString = jsonFormat.printToString(yourProtobufMessage);
Вот ссылка на учебник, который я написал и который использует класс JsonFormat в TypeAdapter, который можно зарегистрировать в объекте GsonBuilder. Затем вы можете использовать методы Gson toJson и fromJson для преобразования прототипа данных в Java и обратно.
Отвечая Жан. Если у нас есть данные protobuf в файле и мы хотим проанализировать их в объект сообщения protobuf, используйте метод слияния TextFormat class. Смотрите приведенный ниже фрагмент:
// Let your proto text data be in a file MessageDataAsProto.prototxt
// Read it into string
String protoDataAsString = FileUtils.readFileToString(new File("MessageDataAsProto.prototxt"));
// Create an object of the message builder
MyMessage.Builder myMsgBuilder = MyMessage.newBuilder();
// Use text format to parse the data into the message builder
TextFormat.merge(protoDataAsString, ExtensionRegistry.getEmptyRegistry(), myMsgBuilder);
// Build the message and return
return myMsgBuilder.build();
Попробуйте JsonFormat.printer().print(MessageOrBuilder)
, он отлично подходит для proto3. Тем не менее, неясно, как преобразовать фактическое сообщение protobuf
(которое предоставляется в виде пакета java по моему выбору, определенного в файле .proto), в объект com.google.protbuf.Message.
Для protobuf 2.5 используйте зависимость:
"com.googlecode.protobuf-java-format" % "protobuf-java-format" % "1.2"
Затем используйте код:
com.googlecode.protobuf.format.JsonFormat.merge(json, builder)
com.googlecode.protobuf.format.JsonFormat.printToString(proto)
Ну, нет никаких ярлыков, чтобы сделать это, согласно моим выводам, но как-то вы
добиться этого в несколько простых шагов
Сначала вы должны объявить компонент типа "ProtobufJsonFormatHttpMessageConverter"
@Bean
@Primary
public ProtobufJsonFormatHttpMessageConverter protobufHttpMessageConverter() {
return new ProtobufJsonFormatHttpMessageConverter(JsonFormat.parser(), JsonFormat.printer());
}
Затем вы можете просто написать класс Utility, такой как ResponseBuilder, потому что он может анализировать запрос по умолчанию, но без этих изменений он не может генерировать ответ Json. а затем вы можете написать несколько методов для преобразования типов ответов в связанный тип объектов.
public static <T> T build(Message message, Class<T> type) {
Printer printer = JsonFormat.printer();
Gson gson = new Gson();
try {
return gson.fromJson(printer.print(message), type);
} catch (JsonSyntaxException | InvalidProtocolBufferException e) {
throw new ApiException(HttpStatus.INTERNAL_SERVER_ERROR, "Response conversion Error", e);
}
}
Затем вы можете вызывать этот метод из вашего класса контроллера как в последней строке, как -
return ResponseBuilder.build(<returned_service_object>, <Type>);
Надеюсь, это поможет вам реализовать protobuf в формате json.
Вот общая версия конвертера Json
package com.intuit.platform.util;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import com.google.protobuf.AbstractMessage.Builder;
import com.google.protobuf.Message;
import com.google.protobuf.MessageOrBuilder;
import com.google.protobuf.util.JsonFormat;
/**
* Generic ProtoJsonUtil to be used to serialize and deserialize Proto to json
*
* @author [email protected]
*
*/
public final class ProtoJsonUtil {
/**
* Makes a Json from a given message or builder
*
* @param messageOrBuilder is the instance
* @return The string representation
* @throws IOException if any error occurs
*/
public static String toJson(MessageOrBuilder messageOrBuilder) throws IOException {
return JsonFormat.printer().print(messageOrBuilder);
}
/**
* Makes a new instance of message based on the json and the class
* @param <T> is the class type
* @param json is the json instance
* @param clazz is the class instance
* @return An instance of T based on the json values
* @throws IOException if any error occurs
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static <T extends Message> T fromJson(String json, Class<T> clazz) throws IOException {
// https://stackoverflow.com/info/27642021/calling-parsefrom-method-for-generic-protobuffer-class-in-java/33701202#33701202
Builder builder = null;
try {
// Since we are dealing with a Message type, we can call newBuilder()
builder = (Builder) clazz.getMethod("newBuilder").invoke(null);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
return null;
}
// The instance is placed into the builder values
JsonFormat.parser().ignoringUnknownFields().merge(json, builder);
// the instance will be from the build
return (T) builder.build();
}
}
Использовать его так же просто, как указано ниже:
GetAllGreetings.Builder allGreetingsBuilder = GetAllGreetings.newBuilder();
allGreetingsBuilder.addGreeting(makeNewGreeting("Marcello", "Hi %s, how are you", Language.EN))
.addGreeting(makeNewGreeting("John", "Today is hot, %s, get some ice", Language.ES))
.addGreeting(makeNewGreeting("Mary", "%s, summer is here! Let go surfing!", Language.PT));
GetAllGreetings allGreetings = allGreetingsBuilder.build();
String json = ProtoJsonUtil.toJson(allGreetingsLoaded);
log.info("Json format: " + json);
GetAllGreetings parsed = ProtoJsonUtil.fromJson(json, GetAllGreetings.class);
log.info("The Proto deserialized from Json " + parsed);