GUIとサーバー間でprotobuf-based通信プロトコルを使用している既存のシステムがあります。ここで永続性を追加したいと思いますが、現時点ではprotobufメッセージはサードパーティのカスタムオブジェクトに直接変換されます。
protoメッセージをjsonに変換する方法はありますか?その後、データベースに保存されました。
N.B.:バイナリprotobufをデータベースに書き込むという考えは、いつかは新しいバージョンとの下位互換性がなくなり、システムをそのように壊してしまう可能性があるため、あまり好きではありません。
現在、 protobuf-Java-format を使用して、Protobufメッセージ(Message
のサブクラス)をJSON形式に変換し、Web APIで送信します。
単純に:
JsonFormat.printToString(protoMessage)
同様の質問への回答 で述べたように、 v3.1. であるため、これはProtocolBuffersのサポートされている機能です。 Javaの場合、拡張モジュール com.google.protobuf:protobuf-Java-util を含め、 JsonFormat を次のように使用します。
JsonFormat.parser().ignoringUnknownFields().merge(json, yourObjectBuilder);
YourObject value = yourObjectBuilder.build();
バイナリprotobufをデータベースに書き込むという考えはあまり好きではありません。それは、いつか新しいバージョンと後方互換性がなくなり、システムをそのように壊してしまう可能性があるからです。
ストレージでprotobufをJSONに変換し、ロード時にprotobufに戻すと、互換性の問題が発生する可能性が高くなりますmore。
とはいえ、protobufをJSONに変換するための多くのライブラリがあり、通常はProtobufリフレクションインターフェイス上に構築されます(Javaリフレクションインターフェイスと混同しないでください。Protobufリフレクションはcom.google.protobuf.Message
インターフェイス)。
Ophir sの回答に追加すると、JsonFormatはprotobuf 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);
GsonBuilderオブジェクトに登録できるTypeAdapterでJsonFormatクラスを使用する tutorial へのリンクを次に示します。その後、GsonのtoJsonおよびfromJsonメソッドを使用して、プロトデータをJavaに変換したり、その逆に変換したりできます。
jean に返信します。ファイルにprotobufデータがあり、それを解析してprotobufメッセージオブジェクトにしたい場合は、mergeメソッド TextFormat クラスを使用します。以下のスニペットを参照してください。
// 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
メッセージ(.protoファイルで定義された私の選択のJavaパッケージとして提供される)を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を宣言する必要があります
@Bean
@Primary
public ProtobufJsonFormatHttpMessageConverter protobufHttpMessageConverter() {
return new ProtobufJsonFormatHttpMessageConverter(JsonFormat.parser(), JsonFormat.printer());
}
次に、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/questions/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's 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);