Today I learn that int64 protobuf is converted to string when we encode it to JSON. This cause some issue on the javascript client side when we assume that the value we receive already in int64 instead of string.

Here is a snippet of the code.

syntax = "proto3";

option go_package="wilianto.com/proto-int64/order";

message Money {
    string currency = 1;
    int64 amount = 2;
    int32 precision = 3;
}
package main

import (
	"fmt"

	"google.golang.org/protobuf/encoding/protojson"
	"wilianto.com/proto-int64/order"
)

func main() {
	money := order.Money{
		Currency:  "SGD",
		Precision: 2,
		Amount:    10050,
	}
	moneyJSON, _ := protojson.Marshal(&money)
	fmt.Println(string(moneyJSON))
	// output
	// {"currency":"SGD","amount":"10050","precision":2}
}

After reading again the protobuf to JSON mapping doc here, it’s expected that int64, fixed64 & uint64 are gonna be converted to decimal string. However, it does not work that way for int32, fixed32 & uint32.

protobuf int64 doc

But why 64-bit number is serialized to string?

Based from what I understand, the issue is not from the protobuf. Instead the main reason is on the JSON protocol itself.

From JSON RFC-8259, we can learn that most of common software implement IEEE 754 double precision floating point to handle such a precision number. It works well to handle integers with range [-(2^53)+1, (2^53)-1] but not with the long PI number for example (3.141592653589793238462643383279). It may indicate potential interoperability problems.

So, to keep the precision between different system/software implementation, JSON with 64-bit precision should be written as string.

Reference