Protobuf-net (de) сериализация бросков десятичных дробей при использовании пользовательского десятичного протоконтракта (взаимодействие C # / C ++)

Скажем, я хочу сериализовать, а затем десериализовать десятичную с использованием 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);
}

Работает нормально. Protobuf-net внутренне использует следующее представление для десятичных дробей (ср. Bcl.proto):

message Decimal {
optional uint64 lo = 1; // the first 64 bits of the underlying value
optional uint32 hi = 2; // the last 32 bis of the underlying value
optional sint32 signScale = 3; // the number of decimal digits, and the sign
}

Теперь скажите, что я определяю предположительно эквивалентный протоконтракт по коду:

[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;
}

…тогда я не могу сериализовать decimal и получить MyDecimal обратно, ни сериализовать MyDecimal и получить decimal назад.

От decimal в MyDecimal:

const decimal originalDecimal = 1.6641007661819458m;
using (var memoryStream = new MemoryStream())
{
Serializer.Serialize(memoryStream, originalDecimal);
memoryStream.Position = 0;

// following line throws a Invalid wire-type ProtoException
Serializer.Deserialize<MyDecimal>(memoryStream);
}

От 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 a Invalid wire-type ProtoException
Serializer.Deserialize<decimal>(memoryStream);
}

Я что-то здесь упускаю?

Я работаю над приложением C ++, которое должно взаимодействовать с C # через буферы протокола и не могу понять, почему десятичная десериализация не удалась.

5

Решение

Это крайний случай «это объект? Или голое значение?». Ты не можешь просто сериализовать intСкажем, в protobuf — вам нужен объект-обертка. Следовательно, для обнаженных значений он делает вид, что значение фактически является полем 1 гипотетического объекта-оболочки. В случае decimalТем не менее, это немного сложно — так как decimal фактически закодирован, как если бы это был объект. Так технически decimal мог записать как обнаженное значение … но: похоже не (оборачивает это) — и я сомневаюсь, что было бы неплохо исправить это на данном этапе.

В принципе, это будет работать намного надежнее, если вместо сериализации обнаженного значения вы сериализуете объект тот имеет ценность. Будет также работать более эффективно (protobuf-net ищет типы, о которых он знает, с обнаженными значениями, в значительной степени резервным сценарием). Например:

[ProtoContract]
class DecimalWrapper {
[ProtoMember(1)]
public decimal Value { get; set; }
}
[ProtoContract]
class MyDecimalWrapper {
[ProtoMember(1)]
public MyDecimal Value { get; set; }
}

Если мы сериализуем эти, они на 100% взаимозаменяемы:

const decimal originalDecimal = 1.6641007661819458m;
using (var memoryStream = new MemoryStream())
{
var obj = new DecimalWrapper { Value = originalDecimal };
Serializer.Serialize(memoryStream, obj);
// or, as it happens (see text) - this is equal to
// 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
}

Собственно, потому что protobuf-net симулирует есть объект, также верно сказать, что Serialize<decimal> будет на 100% совместим с Serialize<MyDecimalWrapper>, но для вашего же здравомыслия это, вероятно, просто Полегче придерживаться простого подхода «всегда сериализировать экземпляр DTO», вместо того, чтобы думать «это DTO?


В заключение: если вы используете взаимодействие, я бы предложил избегать decimal, поскольку это не определено в спецификации protobuf, и разные платформы часто имеют разное значение своего «десятичного» типа. Protobuf-сеть изобретает смысл, в основном, в том, чтобы позволить protobuf-net совершать обходные пути (к себе) в более широком диапазоне DTO, но может быть неудобно анализировать это значение в произвольной платформе. При работе с кроссплатформенностью и использовании decimal, Рекомендую принимая во внимание вещи как double/floatили некоторая фиксированная точность с помощью long/ulongили, может быть, даже просто string,

3

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]