Как получить стандартный вывод сериализации между GO и JS относительно значений по умолчанию?
Я беру часть данных и помещаю ее в protobuf как в GO, так и в JS, а затем кодирую 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)
значение кодирования base64 от GO:
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 вводятся одинаково, но результаты на выходе отличаются.
1 ответ
Ладно, это не идеальное решение, но это не очень приятное решение, пока не появится реальное решение, которое, вероятно, потребует изменения кода в библиотеке protobufjs.
Вместо того, чтобы использовать стандарт Message.encode
позвонил бы goEncode
который удалит любые перечисляемые поля, для которых установлено значение по умолчанию (т. е. значение равно 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();
}