MisterN
@MisterN

Как удобно читать jspb.Message из grpc в JavaScript?

Кто-нибудь может пояснить, как лучше конвертировать мессагу протобафа в JavaScript?
Вот допустим пришло мне сообщение со множеством разных параметров.
export class AnyResponse extends jspb.Message {
  getView(): View | undefined;
  setView(value?: View): OperationResponse;
  hasView(): boolean;
  clearView(): OperationResponse;

  getAnyParam(): string;
  setAnyParam(value: string): OperationResponse;
........

И допустим, где-то в глубине этой кучи данных есть значение value, которое тоже надо получить.
response.getSome()?.getItems()?.getValue()?.toObject() даст нам значение вот в таком виде
"value": {
            "fieldsMap": [
              [
                "id",
                {
                  "nullValue": 0,
                  "numberValue": 1,
                  "stringValue": "",
                  "boolValue": false
                }
              ],
              [
                "anyNname",
                {
                  "nullValue": 0,
                  "numberValue": 0,
                  "stringValue": "Здесь строка",
                  "boolValue": false
                }
              ],

Это не то, что мне нужно, мне было бы гораздо удобнее получить нормальный объект.
Если я применю response.getSome()?.getItems()?.getValue()?.toJavaScript(), то получится то, что нужно.
"value": {
            "id": 1,
            "name": "Здесь строка",
            .......
          }

Но мне необходимо конвертнуть в нормальный объект со значениями весь AnyResponse, который не имеет метода toJavaScript, а он сам и его дети вниз по цепочке являются наследниками jspb.Message и соответственно, имеют только toObject. А value уже google_protobuf_struct_pb.Struct и он может быть обработан через toJavaScript. Вообще-то там массив, и .getValue() нужно еще и циклом перебрать, например. Я пытаюсь упростить код и опустить ненужную информацию, чтобы проблема наглядно сохранилаь.
Апи было спроетировано давно и под json, значения затем сохраняются в стору и класть туда целый объект AnyResponse (jspb.Message) не имеет смысла. Мне очень нужно получить весь объект AnyResponse как простой JavaScript-объект с данными, например через AnyResponse.toObject(), но чтобы там глубоко в value был не fieldsMap, а обычный объект.
Ну вот как можно поступить. Можно

const rowResponse = response.toObject();
        const itemsList = response.getSome()?.getItems()?.getValue()?.map(
            value => ({
                ...value.toObject(),
                value: value.getValue()?.toJavaScript(),
            }),
        );
        const items = {
            ...rowResponse.view.items,
            itemsList: itemsList,
        };

        rowResponse.view.items = items;
        
        return rowResponse;

Но это же маразм, должен быть более простой способ. Я здесь примерно идею показал, на самом деле объект больше и хореография слонее. Плюс приходится изворачиваться с типизацией через Omit, потому что как только items изменился, мы уже не можем типизировать респонс через AnyResponse.AsObject
Должен же быть более простой и красивый способ, нежели это.
Но в голову приходит лишь какой-то треш типа переопределения какого-нибудь метода протобафа. Ведь для Items для нас toObject, возвращающий fieldsMap, абсолютно бесполезен. Однако, переопределять именно Items.toObject бессмысленно. Однако, он представлет из себя стену из вызывов
proto.quark.View.Items.toObject = function (includeInstance, msg) {
    var f, obj = {
      anyNameList: jspb.Message.toObjectList(msg.getAnyNameList(),
          proto.quark.Field.toObject, includeInstance),
      anyTwoNameList: jspb.Message.toObjectList(msg.getAnyTwoNameList(),
          ......

    };
    if (includeInstance) {
      obj.$jspbMessageInstance = msg;
    }
    return obj;
  };

Т.е. обратите внимание, не anyValueList: msg.getAnyValueList().toObjectList, а именно jspb.Message.toObjectList(msg.getAnyNameList(), т.е. везде один общий jspb.Message.toObjectList, нельзя адресно переопределить метод конкретно для anyValueList. В принципе, получится не большая разница между этим и тем, что я предлагал выше. Вы таки будете смеяться, но я попробовал. И за исключением того, что я добавил операций в код, который дергается раз 60 на странице, и все-еще страшный костыль, кода стало гораздо меньше и вообще удобнее.
jspb.Message.toObjectList = function (a, b, c) {
    const rowResult = jspbMessageToObjectListProto.call(this, a, b, c);

    // const svgs = a.findIndex(v=>v && v.getType && v.getType() === 'svg');
    const svgIndex = rowResult.findIndex(v=>v.type === 'svg');
    if(svgIndex >= 0) {
        rowResult[svgIndex].value = a[svgIndex].getValue()?.toJavaScript();
    }

    return rowResult;
}

Но если все-таки оставить этот дурдом, то ну не могло же такого быть, чтобы не было нормальной штатной возможности получить нормальный объект JavaScript? Что я делаю не так?
Самым очевидным вариантов будет взять все и переписать, чтобы у нас все-таки не было такого божественного jspb.Message, но помимо того, что это просто дохрена придется переписывать, стора как бы тут сильно мешает.
Зы как люди упарываются, чтобы просто получить json https://github.com/grpc/grpc-web/issues/315
И это продвинутый фреймворк для общения с сервером от гугл?
  • Вопрос задан
  • 93 просмотра
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы