Java:JSON -> Protobuf 和反向转换
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28545401/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java: JSON -> Protobuf & back conversion
提问by Denis Kulagin
I have an existing system, which is using protobuf-basedcommunication protocol between GUI and server. Now I would like to add some persistence, but at the moment protobuf messages are straight converted to a third-party custom objects.
我有一个现有系统,它在 GUI 和服务器之间使用基于 protobuf 的通信协议。现在我想添加一些持久性,但目前 protobuf 消息直接转换为第三方自定义对象。
Is there a way to convert protomessages to json, which could be then persisted to database.
有没有办法将proto消息转换为json,然后可以将其持久化到数据库中。
N.B.:I don't much like an idea of writing binary protobuf to database, because it can one day become not backward-compatible with newer versions and break the system that way.
注意:我不太喜欢将二进制 protobuf 写入数据库的想法,因为有一天它可能会变得不向后兼容新版本并以这种方式破坏系统。
回答by jas_raj
We are currently using protobuf-java-formatto convert our Protobuf messages (anything subclass of Message
) into a JSON format to send over our web API.
我们目前正在使用protobuf-java-format将我们的 Protobuf 消息( 的任何子类Message
)转换为 JSON 格式以通过我们的 Web API 发送。
Simply do:
简单地做:
JsonFormat.printToString(protoMessage)
回答by Kenton Varda
I don't much like an idea of writing binary protobuf to database, because it can one day become not backward-compatible with newer versions and break the system that way.
我不太喜欢将二进制 protobuf 写入数据库的想法,因为有一天它可能无法向后兼容新版本并以这种方式破坏系统。
Converting protobuf to JSON for storage and then back to protobuf on load is much morelikely to create compatibility problems, because:
将 protobuf 转换为 JSON 进行存储,然后在加载时返回 protobuf更有可能产生兼容性问题,因为:
- If the process which performs the conversion is not built with the latest version of the protobuf schema, then converting will silently drop any fields that the process doesn't know about. This is true both of the storing and loading ends.
- Even with the most recent schema available, JSON <-> Protobuf conversion may be lossy in the presence of imprecise floating-point values and similar corner cases.
- Protobufs actually have (slightly) stronger backwards-compatibility guarantees than JSON. Like with JSON, if you add a new field, old clients will ignore it. Unlike with JSON, Protobufs allow declaring a default value, which can make it somewhat easier for new clients to deal with old data that is otherwise missing the field. This is only a slight advantage, but otherwise Protobuf and JSON have equivalent backwards-compatibility properties, therefore you are not gaining any backwards-compatibility advantages from storing in JSON.
- 如果执行转换的进程不是使用最新版本的 protobuf 模式构建的,那么转换将悄悄删除进程不知道的任何字段。存储端和装载端都是如此。
- 即使使用最新的模式,JSON <-> Protobuf 转换在存在不精确的浮点值和类似的极端情况时也可能是有损的。
- Protobufs 实际上比 JSON 具有(稍微)更强的向后兼容性保证。与 JSON 一样,如果您添加一个新字段,旧客户端将忽略它。与 JSON 不同,Protobufs 允许声明默认值,这可以使新客户端更容易处理否则缺少该字段的旧数据。这只是一个小小的优势,否则 Protobuf 和 JSON 具有等效的向后兼容性属性,因此您不会从存储在 JSON 中获得任何向后兼容性优势。
With all that said, there are many libraries out there for converting protobufs to JSON, usually built on the Protobuf reflection interface (not to be confused with the Java reflection interface; Protobuf reflection is offered by the com.google.protobuf.Message
interface).
尽管如此,有许多库可以将 protobuf 转换为 JSON,通常构建在 Protobuf 反射接口上(不要与 Java 反射接口混淆;Protobuf 反射由com.google.protobuf.Message
接口提供)。
回答by Ophir Radnitz
As mentioned in an answer to a similar question, since v3.1.0this is a supported feature of ProtocolBuffers. For Java, include the extension module com.google.protobuf:protobuf-java-utiland use JsonFormatlike so:
正如在对类似问题的回答中提到的,自v3.1.0以来,这是 ProtocolBuffers 的支持功能。对于 Java,包括扩展模块com.google.protobuf:protobuf-java-util并像这样使用JsonFormat:
JsonFormat.parser().ignoringUnknownFields().merge(json, yourObjectBuilder);
YourObject value = yourObjectBuilder.build();
回答by Moses
Adding to Ophirs answer, JsonFormat is available even before protobuf 3.0. However, the way to do it differs a little bit.
添加到Ophir的答案中,JsonFormat 甚至在 protobuf 3.0 之前就可用。但是,执行此操作的方法略有不同。
In Protobuf 3.0+, the JsonFormat class is a singleton and therefore do something like the below
在 Protobuf 3.0+ 中, JsonFormat 类是一个单例,因此执行如下操作
String jsonString = "";
JsonFormat.parser().ignoringUnknownFields().merge(json,yourObjectBuilder);
In Protobuf 2.5+, the below should work
在 Protobuf 2.5+ 中,以下应该可以工作
String jsonString = "";
JsonFormat jsonFormat = new JsonFormat();
jsonString = jsonFormat.printToString(yourProtobufMessage);
Here is a link to a tutorialI wrote that uses the JsonFormat class in a TypeAdapter that can be registered to a GsonBuilder object. You can then use Gson's toJson and fromJson methods to convert the proto data to Java and back.
这是我编写的教程的链接,该教程在可以注册到 GsonBuilder 对象的 TypeAdapter 中使用 JsonFormat 类。然后,您可以使用 Gson 的 toJson 和 fromJson 方法将 proto 数据转换为 Java 并返回。
Replying to jean. If we have the protobuf data in a file and want to parse it into a protobuf message object, use the merge method TextFormatclass. See the below snippet:
回复让. 如果我们在文件中有 protobuf 数据,并且想将其解析为 protobuf 消息对象,请使用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();
回答by jean
Try JsonFormat.printer().print(MessageOrBuilder)
, it looks good for proto3. Yet, it is unclear how to convert the actual protobuf
message (which is provided as the java package of my choice defined in the .proto file) to a com.google.protbuf.Message object.
试试看JsonFormat.printer().print(MessageOrBuilder)
,proto3 看起来不错。然而,尚不清楚如何将实际protobuf
消息(作为我在 .proto 文件中定义的我选择的 java 包提供)转换为 com.google.protbuf.Message 对象。
回答by Henry
For protobuf 2.5, use the dependency:
对于 protobuf 2.5,使用依赖项:
"com.googlecode.protobuf-java-format" % "protobuf-java-format" % "1.2"
Then use the code:
然后使用代码:
com.googlecode.protobuf.format.JsonFormat.merge(json, builder)
com.googlecode.protobuf.format.JsonFormat.printToString(proto)
回答by Bharat
Well, there is no shortcut to do it as per my findings, but somehow you
an achieve it in few simple steps
好吧,根据我的发现,没有捷径可以做到,但不知何故,您
可以通过几个简单的步骤实现它
First you have to declare a bean of type 'ProtobufJsonFormatHttpMessageConverter'
首先,您必须声明一个“ProtobufJsonFormatHttpMessageConverter”类型的bean
@Bean
@Primary
public ProtobufJsonFormatHttpMessageConverter protobufHttpMessageConverter() {
return new ProtobufJsonFormatHttpMessageConverter(JsonFormat.parser(), JsonFormat.printer());
}
Then you can just write an Utility class like ResponseBuilder, because it can parse the request by default but without these changes it can not produce Json response. and then you can write few methods to convert the response types to its related object type.
然后你可以写一个像ResponseBuilder这样的Utility类,因为它默认可以解析请求,但没有这些变化就不能产生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);
}
}
Then you can call this method from your controller class as last line like -
然后你可以从你的控制器类调用这个方法作为最后一行 -
return ResponseBuilder.build(<returned_service_object>, <Type>);
Hope this will help you to implement protobuf in json format.
希望这能帮助您以 json 格式实现 protobuf。
回答by Marcello de Sales
Generics Solution
泛型解决方案
Here's a generic version of Json converter
这是 Json 转换器的通用版本
package com.github.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();
}
}
Using it is as simple as follows:
使用它很简单,如下所示:
Message instance
消息实例
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();
To Json Generic
到 Json 泛型
String json = ProtoJsonUtil.toJson(allGreetingsLoaded);
log.info("Json format: " + json);
From Json Generic
来自 Json 泛型
GetAllGreetings parsed = ProtoJsonUtil.fromJson(json, GetAllGreetings.class);
log.info("The Proto deserialized from Json " + parsed);