protobuf3 JsonFormat InvalidProtocolBufferException Неизвестный символ

Я пытаюсь преобразовать json в объект прото-сообщения, но если в json есть некоторые символы, такие как $ или . или _ я получаю исключение InvalidProtocolBufferException. Мой прототип:

syntax = "proto3";

package messages;
option java_multiple_files = true;

message Main {
    A a = 1;
}

message A {
    repeated B b = 1;
}

message B {
    repeated C c = 1;
}

message C {
    map<string, bytes> d = 1;
}

Java-код:

 String json = "{\n" +
        "  \"a\" : {\n" +
        "    \"b\" : [ {\n" +
        "      \"c\" : [ {\n" +
        "        \"d\" : {\n" +
        "          \"money\" : [ \"100$\" ]\n" +
        "        }\n" +
        "      } ]\n" +
        "    } ]\n" +
        "  }\n" +
        "}";
    Main.Builder builder = Main.newBuilder();
    JsonFormat.parser().merge(json, builder);
    System.out.println(builder.build());

выход:

  com.google.protobuf.InvalidProtocolBufferException: com.google.common.io.BaseEncoding$DecodingException: Unrecognized character: $

        at com.google.protobuf.util.JsonFormat$ParserImpl.merge(JsonFormat.java:1065)
        at com.google.protobuf.util.JsonFormat$Parser.merge(JsonFormat.java:273)

person idmitriev    schedule 17.02.2017    source источник
comment
Не могли бы вы добавить проблемное определение json и protobuf типа User?   -  person Michał Przybylak    schedule 17.02.2017
comment
Я сделал это, пожалуйста, проверьте   -  person idmitriev    schedule 17.02.2017


Ответы (1)


На мой взгляд, ваша проблема связана с объявлением типа C, внутри него в proto definiton у вас есть map<string,bytes> d = 1; Который на стороне Java будет: Map<String, ByteString> И в вашем Json у вас есть что-то, что «похоже» на карту String-String.

Давайте проведем быстрый эксперимент и попробуем преобразовать ваш основной тип в json:

Main main = Main.newBuilder().setA(
        A.newBuilder().addB(
                B.newBuilder().addC(
                        C.newBuilder().putD("money", 
                                ByteString.copyFrom("100$".getBytes()))
                )
        )
).build();

System.out.println(JsonFormat.printer().print(main));

Результат будет

{
  "a": {
    "b": [{
      "c": [{
        "d": {
          "money": "MTAwJA=="
        }
      }]
    }]
  }
}

Как видите, у нас не 100$, а закодированная версия MTAwJA==, и если вы попытаетесь преобразовать этот json в объект с помощью protobuf, у вас не будет ошибки.

Так что, на мой взгляд, либо кодируйте значения в вашей карте d во время преобразования в json (или конвертируйте в json с помощью protobuf), либо измените объявление protobuf

person Michał Przybylak    schedule 17.02.2017
comment
Спасибо, Михал, я понял, как и вы, я думаю, что в этом случае невозможно преобразовать json в proto с помощью JsonFormat. Я просто хочу сопоставить java.lang.Object с прото-сообщением. У меня есть объект данных с данными Map‹String, Object›, и мне нужно отправить его с помощью protobuf. - person idmitriev; 17.02.2017
comment
На мой взгляд, это невозможно без дополнительной обработки json (на стороне отправителя) или без изменения определения proto. Возможно, что в вашем случае на стороне отправителя вы также можете использовать protobuf для сериализации в json? - person Michał Przybylak; 18.02.2017
comment
да, с обеих сторон я использую protobuf. Я все равно могу это сделать, но я еще не знаю правильного пути. Теперь я решил отобразить его как строку карты, байты›, поскольку я получил ответ оттуда - stackoverflow.com/questions/41878400/ - person idmitriev; 18.02.2017
comment
Хорошо, извините - я не понял вас в первую очередь. Я предполагаю, что ваш объект не может стать объектом protobuf? Если это не так, вероятно (как это было упомянуто в ссылке, которую вы дали), вам нужно будет сериализовать свой объект в массив байтов - person Michał Przybylak; 18.02.2017
comment
Ага, я так и сделал - map‹string,bytes› - person idmitriev; 18.02.2017
comment
Суть в том, что я не могу использовать это преобразование json со строковым типом (100 $) в тип сообщения byte‹string, bytes›. Я должен использовать создание сообщения вручную - ByteStream.copyFrom(100$.getBytes()) - person idmitriev; 18.02.2017