2013-05-15 16 views
5

Nói rằng tôi muốn serialize, sau đó deserialize một số thập phân sử dụng protobuf-net:Protobuf-net (de) serialization thập phân ném khi sử dụng hợp đồng proto tùy chỉnh số thập phân (C#/C++ interop)

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

Nó hoạt động tốt . Protobuf-net nội bộ sử dụng các đại diện sau cho số thập phân (x 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 
} 

Bây giờ nói rằng tôi xác định một hợp đồng được cho là tương đương proto theo mã:

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

... sau đó tôi có thể' t serialize a decimal và nhận lại số MyDecimal, cũng không được làm lại số MyDecimal và nhận lại số decimal.

Từ 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); 
} 

Từ 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); 
} 

Tôi có thiếu cái gì ở đây?

Tôi đang làm việc trên ứng dụng C++ cần giao tiếp với C# một thông qua bộ đệm giao thức và không thể tìm ra lý do tại sao deserializations thập phân thất bại.

Trả lời

3

Đây là trường hợp cạnh của "nó là đối tượng? Hoặc giá trị khỏa thân?". Bạn không thể chỉ serialize một int, giả sử, trong protobuf - bạn cần một đối tượng bao bọc. Đối với các giá trị khỏa thân, do đó, nó giả vờ rằng giá trị thực sự là trường 1 của một đối tượng bọc giả thuyết. Trong trường hợp của decimal, mặc dù, điều này là một chút khôn lanh - vì decimal thực sự được mã hóa như thể nó là một đối tượng. Vì vậy, về mặt kỹ thuật decimalthể được viết như một giá trị trần truồng ... nhưng: có vẻ như nó không phải là (nó được gói nó) - và tôi nghi ngờ nó sẽ là một ý tưởng tốt để khắc phục rằng ở giai đoạn này .

Về cơ bản, điều này sẽ làm việc rất nhiều cách đáng tin cậy hơn nếu thay vì serializing một giá trị trần truồng, bạn serialize một đối tượng rằng một giá trị. Nó sẽ cũng hoạt động hiệu quả hơn (protobuf-net tìm kiếm các loại mà nó biết, với các giá trị khỏa thân rất nhiều một kịch bản dự phòng). Ví dụ:

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

Nếu chúng ta serialize những, họ là 100% thay thế cho nhau:

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 
} 

Trên thực tế, bởi vì protobuf-net giả vờ có một đối tượng, nó cũng là sự thật để nói rằng Serialize<decimal> sẽ tương thích 100% với Serialize<MyDecimalWrapper>, nhưng đối với sự tỉnh táo của bạn, có lẽ chỉ cần dễ dàng hơn để tuân theo cách tiếp cận "luôn luôn tuần tự hóa một trường hợp DTO" đơn giản, thay vì phải suy nghĩ " đây là một DTO? hay nó là một giá trị trần trụi?"


Như một ý nghĩ cuối cùng: nếu bạn đang sử dụng interop, tôi sẽ đề nghị tránh decimal, vì đó không được định nghĩa trong đặc tả protobuf, và các nền tảng khác nhau thường có một ý nghĩa khác nhau của họ 'thập phân' loại. protobuf-net phát minh ý nghĩa, chủ yếu để cho phép protobuf-net thực hiện vòng tròn (chính nó) phạm vi rộng hơn của DTO, nhưng có thể khó phân tích giá trị đó thành nền tảng tùy ý. Khi làm việc trên nền tảng và sử dụng decimal, tôi khuyên bạn nên xem xét những thứ như double/float hoặc một số độ chính xác cố định qua long/ulong hoặc thậm chí chỉ string.

+1

Cảm ơn câu trả lời. Tất cả điều này có ý nghĩa. Một số ý kiến: 1. Chúng tôi không gửi số thập phân trần truồng, chúng tôi sử dụng chúng trong DTO. 2. Có, mẫu của tôi không hoàn toàn minh họa cho vấn đề tôi đang gặp; trên thực tế, tôi đã tìm ra rằng việc tuần tự hóa/deserialization của một 'DecimalWrapper' chứa một' decimal' đến/từ 'MyDecimalWrapper' chứa' MyDecimal' hoạt động như mong đợi. 3. Trong trường hợp của tôi, tôi có một số thập phân trong C++ có thể được biểu diễn dưới dạng tương thích với C# lo/hi/signscale; các số thập phân protobuf-net trông giống như một hợp đồng tốt. –

+1

4. Vấn đề dường như được gây ra bởi thực tế là bcl.proto thập phân đã được xác định với một 'sint32' SignScale, và protobuf-net đã viết/đọc một' uint32' đến/từ dòng. Nếu serialization xuất hiện trong C++ (ghi 'sint32'), thì cách protobuf-net đọc nó tạo ra một giá trị không hợp lệ. Tôi thậm chí không chắc chắn làm thế nào để xử lý 'sint32' giá trị trong C#. –

+0

@RomainVerdier ah, 'sint32' vs' uint32' ... damn, đó là một nỗi đau. Các mã hóa zig-zag là dễ dàng, nhưng nếu tôi đã thực hiện các hợp đồng và mã nói những điều khác nhau thì đó là gây phiền nhiễu –

Các vấn đề liên quan