grpc / grpc-dotnet

gRPC for .NET
Apache License 2.0
4.19k stars 769 forks source link

how to generate "decimal" type with Protobuf compiler #424

Closed kevindaizj closed 5 years ago

JamesNK commented 5 years ago

There isn't a built in decimal type. Discussion here: https://github.com/protocolbuffers/protobuf/issues/4406

You could send decimals as strings. Or have a custom object with its component parts: https://stackoverflow.com/a/371856/11829

ThatRendle commented 5 years ago

I've just written about this in some documentation I'm working on. The best suggestion I saw was to use Google's money.proto without the currency field. Would probably look like this:

package CustomTypes;

// Example: 12345.6789 -> { units = 12345, nanos = 678900000 }
message Decimal {

    // Whole units part of the amount
    int64 units = 1;

    // Nano units of the amount (10^-9)
    // Must be same sign as units
    sfixed32 nanos = 2;
}

Conversion to/from BCL decimal as implicit operators in the partial generated class:

namespace CustomTypes
{
    public partial class Decimal
    {
        private const decimal NanoFactor = 1_000_000_000;
        public GrpcDecimal(long units, int nanos)
        {
            Units = units;
            Nanos = nanos;
        }

        public long Units { get; }
        public int Nanos { get; }

        public static implicit operator decimal(CustomTypes.Decimal grpcDecimal)
        {
            return grpcDecimal.Units + grpcDecimal.Nanos / NanoFactor;
        }

        public static implicit operator CustomTypes.Decimal(decimal value)
        {
            var units = decimal.ToInt64(value);
            var nanos = decimal.ToInt32((value - units) * NanoFactor);
            return new CustomTypes.Decimal(units, nanos);
        }
    }
}
JamesNK commented 5 years ago

I think the linked discussion is the best place to talk about decimal and Protobuf.