【问题标题】:Long values in objects not serialized and deserialized properly when using proto3 in Java在 Java 中使用 proto3 时,对象中的长值未正确序列化和反序列化
【发布时间】:2018-07-27 19:44:49
【问题描述】:

我正在尝试使用 proto3 对 java 中的对象进行序列化和反序列化。这是我在 proto 中的对象的样子

option java_multiple_files = true;
option java_package = "com.project.dataModel";
option java_outer_classname = "FlowProto";


// The request message containing the user's name.
message Flow {
    string subscriberIMSEI = 1;
    string destinationIP = 2;
    uint64 txBytes = 3;
    uint64 rxBytes = 4;
    uint64 txPkts = 5;
    uint64 rxPkts = 6;
    uint64 startTimeInMillis = 7;
    uint64 endTimeInMillis = 8;
    string asnNumber = 9;
    string asnName = 10;
    string asnCountryCode = 11;

}

这是我在 java 中的序列化和反序列化的样子

public class Test {

    public static void main(String[] args) throws Exception {

        Flow flow =
                Flow.newBuilder().setAsnName("abc")
                        .setEndTimeInMillis(123456789L)
                        .setStartTimeInMillis(123456789L)
                .setDestinationIP("1.1.1.1")
                .setTxBytes(1L)
                .setRxBytes(1L)
                .setTxPkts(1L)
                .setRxPkts(1L)
                .setAsnName("blah")
                .setAsnCountryCode("blah")
                .build();

        byte[] flowByteArray = flow.toByteArray();

        String flowString = flow.toByteString().toStringUtf8();

        System.out.println("Parsed from ByteArray:" + Flow.parseFrom(flowByteArray).getEndTimeInMillis());
        System.out.println("Parsed from ByteString:" + Flow.parseFrom(ByteString.copyFromUtf8(flowString))
                .getEndTimeInMillis());
    }
}

我的输出如下

Parsed from ByteArray:123456789
Parsed from ByteString:-4791902657223630865

当我尝试使用 ByteString 和 utf-8 路由进行序列化和反序列化时,我哪里出错了?

谢谢!

【问题讨论】:

    标签: java protocol-buffers proto proto3


    【解决方案1】:

    您看到问题的原因是您的序列化字节数组已损坏。发生这种情况是因为 UTF-8 是一种可变长度编码,并且转换为 UTF-8 字符串会更改原始数组中的字节。当您执行flow.toByteString().toStringUtf8() 时,原始字节串中的一个字节可能会转换为三个具有不同值的新字节。然后,当您执行ByteString.copyFromUtf8(flowString) 时,字节更改不会撤消,因为该代码行实际上只是检索转换后的 UTF-8 字节,而不是您输入的原始字节。

    这是一个小测试,说明您遇到的问题

    @Test
    public void byteConsistency() {
      byte[] vals = new byte[] {0, 110, -1};
      ByteString original = ByteString.copyFrom(vals);
      ByteString newString = ByteString.copyFromUtf8(original.toStringUtf8());
    
      for (int index = 0; index < newString.size(); index++) {
        System.out.println(newString.byteAt(index));
      }
    }
    

    你会期望这段代码输出

    0
    110
    -1
    

    但它实际上输出

    0
    110
    -17
    -65
    -67
    

    这是因为 UTF-8 可能规定 -1 (0xFF) 字节应编码为三个字节 [-17, -65, -67]。

    总而言之,在处理 protobuf 时,不要将序列化对象转换为 UTF-8 字符串。仅使用原始字节进行序列化和反序列化。如果您尝试将其转换为 UTF-8 字符串,则序列化字节将被损坏,您将无法对其进行反序列化。

    【讨论】:

    • 是的,我们使用的是 Pentaho,它不支持传递字节 []。我最终使用 Base64 编码将 byte[] 转换为字符串。
    猜你喜欢
    • 2014-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-02
    • 1970-01-01
    • 2021-04-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多