Длинные значения в объектах не сериализованы и не десериализованы должным образом при использовании proto3 в Java

Я пытаюсь сериализовать и десериализовать объект в java с помощью proto3. Вот как выглядит мой объект в прототипе

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 для сериализации и десериализации?

Спасибо!


person Abdul Rahman    schedule 27.07.2018    source источник


Ответы (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, сериализованные байты будут повреждены, и вы не сможете их десериализовать.

person ilooner    schedule 29.07.2018
comment
Да, мы используем Pentaho, который не поддерживает передачу byte []. В итоге я использовал кодировку Base64 для преобразования byte [] в строку. - person Abdul Rahman; 30.07.2018