0

我取一段数据并将其放在 GO 和 JS 中的 protobuf 中,然后在每个平台上对 PB 进行编码,得到的序列化值不同。由于我们使用编码值进行签名和散列,因此它们匹配至关重要。据我所知,我认为不同之处在于 JS 在编码输出中包含默认值,而 GO 没有。

两个平台都从同一段 JSON 开始。这是GO:

listing := new(pb.Listing)
err = jsonpb.UnmarshalString(string(jsonListing), listing)
ser, err := proto.Marshal(listing)
sEnc := b64.StdEncoding.EncodeToString(ser)

来自 GO 的 base64 编码值为:

ChR0ZXN0LXRlc3QtdGVzdC1taWxseRLEAQouUW1RMlRoQkw2emNZeEJzQ0gyZlVWM0VVaVBNM3RZbWRuUDNxN2prZTIxdUJNcBpJCiQIARIg1JRiC99XTy49u47TrmPhebH2IoWanvr9rfG2+cj8O4YSIQLJRSKlbpvhAB4nyf2yr0gTbVTXwn8uL41usco/cwtyliJHMEUCIQDqnEyTrFKKNY0FRlbn9wC4+69ozF8C3meKcLQG36nseQIgfJs1dJdFTSM2lGg7hQ68O1PVjAZHWO2XRaogo3OMeUgaKwgEIgYI4LSc/wcqA0xUQyoDQlRDKgNCQ0gqA1pFQzIDVVNEQLgIUIDC1y8ioAIKFFRFU1QgVEVTVCBURVNUIG1pbGx5IGQ6/gEKDG11cmFrYW1pLmpwZxIuUW1WeVZIOFJhbTZNZTNpaHlLZ2p6SnNNMlhaeG5QajZQS1NmbmVSRmY4WmFhRBouUW1laFFoMlNDeVZuWXpZNTduVFAzOWRrbUU3Z0t5ekpHeUhUTko0dXpDM2QyRCIuUW1YcTFSTEt0d2E3VmNSemFhN0dTWEtoVWdIYnBicUhNZWhVS2RDeVVTV1hvNyouUW1VeHlBdHYzdzgxWVFnaEFtckVHTThpbjRYU01QNkROZEVnY1RqNm12UXRjMzIuUW1TODhUcVgySzlwU1VvdnFjczNXbkdhUDFRQjdoTXNSUHdMZFVXNmR5UzRoTFIDTkVXYgAqKQoMVVNBIHNoaXBzdGVyEAEaAuoBKhMKCFN0YW5kYXJkEBkaAzUtNyAK

下面是 JS 的作用:

const ListingPB = getProtoContractsRoot().lookupType('Listing');
const listingPB = ListingPB.fromObject(jsonListing);
const ser = ListingPB
  .encode(listingPB)
  .finish();

结果是:

ChR0ZXN0LXRlc3QtdGVzdC1taWxseRLGAQouUW1RMlRoQkw2emNZeEJzQ0gyZlVWM0VVaVBNM3RZbWRuUDNxN2prZTIxdUJNcBIAGkkKJAgBEiDUlGIL31dPLj27jtOuY+F5sfYihZqe+v2t8bb5yPw7hhIhAslFIqVum+EAHifJ/bKvSBNtVNfCfy4vjW6xyj9zC3KWIkcwRQIhAOqcTJOsUoo1jQVGVuf3ALj7r2jMXwLeZ4pwtAbfqex5AiB8mzV0l0VNIzaUaDuFDrw7U9WMBkdY7ZdFqiCjc4x5SBo6CAQQABgAIggI4LSc/wcQACoDTFRDKgNCVEMqA0JDSCoDWkVDMgNVU0Q6AEC4CEoAUIDC1y9dAAAAACKxAgoUVEVTVCBURVNUIFRFU1QgbWlsbHkSABoAIGQoADr+AQoMbXVyYWthbWkuanBnEi5RbVZ5Vkg4UmFtNk1lM2loeUtnanpKc00yWFp4blBqNlBLU2ZuZVJGZjhaYWFEGi5RbWVoUWgyU0N5Vm5Zelk1N25UUDM5ZGttRTdnS3l6Skd5SFROSjR1ekMzZDJEIi5RbVhxMVJMS3R3YTdWY1J6YWE3R1NYS2hVZ0hicGJxSE1laFVLZEN5VVNXWG83Ki5RbVV4eUF0djN3ODFZUWdoQW1yRUdNOGluNFhTTVA2RE5kRWdjVGo2bXZRdGMzMi5RbVM4OFRxWDJLOXBTVW92cWNzM1duR2FQMVFCN2hNc1JQd0xkVVc2ZHlTNGhMTQAAAABSA05FV2IGEgAYACAAKikKDFVTQSBzaGlwc3RlchABGgLqASoTCghTdGFuZGFyZBAZGgM1LTcgCkoAUgA=

...这与 GO 提出的不同。

如果我将这两个 base64 字符串都带入 JS,将它们解码为 PB,然后toJSON()将 PB 解码并查看两个对象的差异,看起来不同之处在于 JS 正在序列化默认值而 GO 不是(JS 开启正确的)。

在此处输入图像描述

我试过这样序列化 JS,但结果是一样的:

    const ser =
      ListingPB
        .encode(ListingPB.toObject(listingPB, { defaults: false }))
        .finish();

那么,有什么方法可以使两个平台之间的输出保持一致?原始 JSON 输入是相同的,但输出的结果不同。

4

1 回答 1

0

好的,这不是理想的解决方案,但在真正的解决方案出现之前,它的工作并不是那么好,这可能需要对 protobufjs 库进行代码更改。

而不是使用标准Message.encode,您将调用goEncodewhich 将删除任何设置为其默认值的枚举字段(即值为 0)。

function convertFields(obj, PB) {
  const converted = Object
    .keys(obj)
    .reduce((converted, field) => {
      const fieldType = PB.fields[field];

      if (fieldType) {
        const FieldPB = PB[fieldType.type];

        if (fieldType.resolvedType instanceof protobuf.Enum) {
          // If the field is an Enum and it's set to the first item (default item)
          // return the nothing so the field is not included in the resulting object.
          if (FieldPB && (obj[field] === 0)) {
            return converted;
          }
        } else if (fieldType.repeated) {
          converted[field] = obj[field]
            .map(fieldObj => (
              FieldPB ?
                convertFields(fieldObj, FieldPB) : fieldObj
            ));
        } else if (FieldPB) {
          converted[field] = convertFields(obj[field], FieldPB);
          return converted;
        }
      }

      converted[field] = obj[field];
      return converted;
    }, {});

  return converted;  
}

/*
 * Will encode a protobuf in a way that matches how GO does it.
 *
 * @param {object} message - A plain javascript object or protobuf instance.
 * @param {object} PB - The protobuf class that corresponds to the provided message.
 *
 * @returns {Uint8Array} - The encoded message.
 */
export function goEncode(message, PB) {
  let messageObj = message;

  if (message instanceof protobuf.Message) {
    messageObj = PB.toObject(message, {
      defaults: false,
      arrays: false,
      objects: false,
    });
  }

  const converted = convertFields(messageObj, PB);

  return PB.encode(converted).finish();
}
于 2019-08-26T23:22:34.157 回答