【问题标题】:How to convert from Json to Protobuf?如何从 Json 转换为 Protobuf?
【发布时间】:2016-11-19 06:14:44
【问题描述】:

我是使用 protobuf 的新手,想知道是否有一种简单的方法可以将 json 流/字符串转换为 Java 中的 protobuf 流/字符串?

例如,

protoString = convertToProto(jsonString)

我有一个要解析为 protobuf 消息的 json 字符串。所以,我想先把json字符串转成protobuf,然后调用Message.parseFrom()就可以了。

提前感谢您的帮助!

【问题讨论】:

  • 你可能想看看FlatBuffers
  • "是将json流/字符串转换为protobuf流/字符串的简单方法?"可能不是。它们的工作方式不同; protobuf 字段实际上并未在序列化表示中命名,但它们是按顺序排列的。 JSON 几乎相反;字段已命名,但顺序通常无关紧要。您可以做的是获取生成的 protobuf 类并使用 Jackson mixins 对其进行注释并解析为它们。

标签: java json protocol-buffers


【解决方案1】:

使用 proto3,您可以使用 JsonFormat 执行此操作。它直接从 JSON 表示中解析,因此无需单独调用 MyMessage.parseFrom(...)。这样的事情应该可以工作:

JsonFormat.parser().merge(json_string, builder);

【讨论】:

  • 当一个值无效时(比如proto是布尔值,但json值是整数),有没有办法配置解析器忽略该字段或将该字段设置为null,而不是抛出@987654324 @?
  • 这似乎不适用于我的班级 - 我收到 com.google.protobuf.InvalidProtocolBufferException: JsonObject - 有什么建议吗?
【解决方案2】:
//You can use this for converting your input json to a Struct / any other Protobuf Class    

import com.google.protobuf.Struct.Builder;
import com.google.protobuf.Struct;
import com.google.protobuf.util.JsonFormat;
import org.json.JSONObject;

JSONObject parameters = new JSONObject();

Builder structBuilder = Struct.newBuilder();
JsonFormat.parser().merge(parameters.toString(), structBuilder);

// Now use the structBuilder to pass below (I used it for Dialog Flow V2 Context Management)

【讨论】:

  • 虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。
【解决方案3】:

在线服务: https://json-to-proto.github.io/

此工具可立即将 JSON 转换为 Protobuf。在左侧粘贴一个 JSON 结构,右侧将生成等效的 Protobuf,您可以将其粘贴到您的程序中。脚本必须做出一些假设,所以请仔细检查输出!

【讨论】:

  • 从示例输入创建一个 .proto schema。听起来 OP 想要一个 payload,而不是模式
【解决方案4】:

由于有人在遵循 Adam 的建议时询问有关获取异常“com.google.protobuf.InvalidProtocolBufferException: JsonObject”的问题——我遇到了同样的问题。原来这是由于 google protobuf 时间戳。它们被序列化为包含两个字段“秒”和“纳米”的对象,因为这不是生产代码,我只是通过使用杰克逊解析 JSON 来解决这个问题,递归地遍历 JSON 对象并从对象到按照 RFC 3339 格式化的字符串,然后我将其序列化并使用 Adam 所示的 protobuf JSON 解析器。这解决了这个问题。这是我写的一些一次性代码(在我的例子中,所有时间戳字段都包含“时间戳”这个词,这可能更健壮,但我不在乎):

public Map<String, Object> fixJsonTimestamps(Map<String, Object> inMap) {
    Map<String, Object> outMap = new HashMap<>();
    for(String key : inMap.keySet()) {
        Object val = inMap.get(key);
        if(val instanceof Map) {
            Map<String, Object> valMap = (Map<String, Object>)val;
            if(key.toLowerCase().contains("timestamp") && 
                    valMap.containsKey("seconds") && valMap.containsKey("nanos")) {
                if(valMap.get("seconds") != null) {
                    ZonedDateTime d = ZonedDateTime.ofInstant(Instant.ofEpochSecond((int)valMap.get("seconds")).plusNanos((int)valMap.get("nanos")),
                            ZoneId.of("UTC"));
                    val = d.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"));
                }
                
            } else {
                val = fixJsonTimestamps(valMap);
            }
        } else if(val instanceof List && ((List) val).size() > 0 && 
                ((List) val).get(0) instanceof Map) {
            List<Map<String, Object>> outputList = new ArrayList<>();
            for(Map item : (List<Map>)val) {
                outputList.add(fixJsonTimestamps(item));
            }
            val = outputList;
        }
        outMap.put(key, val);
    }
    return outMap;
}

不是最理想的解决方案,但它适用于我正在做的事情,我想我看到有人推荐使用不同的时间戳类。

【讨论】:

  • 嘿@Benjamin McCord。欢迎来到 Stack Overflow,感谢您的贡献。您是否尝试过通过编辑而不是发布自己的来改进亚当的答案?这肯定会有助于使亚当的答案更完整,并且更多的人会看到您的贡献,因为亚当的投票最多。
猜你喜欢
  • 2011-10-23
  • 2020-04-19
  • 2019-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-16
相关资源
最近更新 更多