Protobuf-net (de)serialization of decimals throws when using custom decimal proto contract (C#/C++ interop) -
say want serialize, deserialize decimal using protobuf-net:
const decimal originaldecimal = 1.6641007661819458m; using (var memorystream = new memorystream()) { serializer.serialize(memorystream, originaldecimal); memorystream.position = 0; var deserializeddecimal = serializer.deserialize<decimal>(memorystream); assert.areequal(originaldecimal, deserializeddecimal); } it works fine. protobuf-net internally uses following representation decimals (cf. bcl.proto):
message decimal { optional uint64 lo = 1; // first 64 bits of underlying value optional uint32 hi = 2; // last 32 bis of underlying value optional sint32 signscale = 3; // number of decimal digits, , sign } now define supposedly equivalent proto contract code:
[protocontract] public class mydecimal { [protomember(1, isrequired = false)] public ulong lo; [protomember(2, isrequired = false)] public uint hi; [protomember(3, isrequired = false)] public int signscale; } ...then can't serialize decimal , mydecimal back, nor serialize mydecimal , decimal back.
from decimal mydecimal:
const decimal originaldecimal = 1.6641007661819458m; using (var memorystream = new memorystream()) { serializer.serialize(memorystream, originaldecimal); memorystream.position = 0; // following line throws invalid wire-type protoexception serializer.deserialize<mydecimal>(memorystream); } from mydecimal decimal:
var mydecimal = new mydecimal { lo = 0x003b1ee886632642, hi = 0x00000000, signscale = 0x00000020, }; using (var memorystream = new memorystream()) { serializer.serialize(memorystream, mydecimal); memorystream.position = 0; // following line throws invalid wire-type protoexception serializer.deserialize<decimal>(memorystream); } am missing here?
i'm working on c++ application needs communicate c# 1 through protocol buffers , can't figure why decimal deserializations fail.
this edge case of "is object? or naked value?". can't just serialize int, say, in protobuf - need wrapper object. naked values, therefore, pretends value field 1 of hypothetical wrapper object. in case of decimal, though, bit tricky - since decimal encoded though object. technically decimal could written naked value... but: looks isn't (it wrapping it) - , doubt idea rectify @ stage.
basically, work lot more reliably if instead of serializing naked value, serialize object has value. also work more efficiently (protobuf-net looks types knows about, naked values fallback scenario). example:
[protocontract] class decimalwrapper { [protomember(1)] public decimal value { get; set; } } [protocontract] class mydecimalwrapper { [protomember(1)] public mydecimal value { get; set; } } if serialize these, 100% interchangeable:
const decimal originaldecimal = 1.6641007661819458m; using (var memorystream = new memorystream()) { var obj = new decimalwrapper { value = originaldecimal }; serializer.serialize(memorystream, obj); // or, happens (see text) - equal // serializer.serialize(memorystream, originaldecimal); memorystream.position = 0; var obj2 = serializer.deserialize<mydecimalwrapper>(memorystream); console.writeline("{0}, {1}, {2}", obj2.value.lo, obj2.value.hi, obj2.value.signscale); // ^^^ 16641007661819458, 0, 32 memorystream.setlength(0); serializer.serialize(memorystream, obj2); memorystream.position = 0; var obj3 = serializer.deserialize<decimalwrapper>(memorystream); bool eq = obj3.value == obj.value; // true } actually, because protobuf-net pretends there object, true serialize<decimal> 100% compatible serialize<mydecimalwrapper>, own sanity easier stick simple "always serialize dto instance" approach, rather having think "is dto? or naked value?"
as final thought: if using interop, suggest avoiding decimal, since not defined in protobuf specification, , different platforms have different meaning of "decimal" type. protobuf-net invents meaning, allow protobuf-net round-trip (to itself) wider range of dtos, may awkward parse value arbitrary platform. when working cross-platform , using decimal, recommend considering things double/float, or fixed precision via long/ulong, or maybe string.
Comments
Post a Comment