2009-03-10 39 views
9

Tôi đang tìm hỗ trợ ngôn ngữ của tuần tự hóa trong C#. Tôi có thể lấy được từ ISerializable và thực hiện tuần tự hóa bằng cách sao chép các giá trị thành viên trong một bộ đệm byte. Tuy nhiên, tôi thích một cách tự động hơn giống như người ta có thể làm trong C/C++.Byte để tuần tự hóa byte của một cấu trúc trong C#

Xét đoạn mã sau:

using System; 
using System.Text; 
using System.Runtime.Serialization; 
using System.Runtime.Serialization.Formatters.Binary; 
using System.IO; 

namespace XBeeHelper 
{ 
    class XBee 
    { 
     [Serializable()] 
     public struct Frame<FrameType> where FrameType : struct 
     { 
      public Byte StartDelimiter; 
      public UInt16 Lenght; 
      public Byte APIIdentifier; 
      public FrameType FrameData; 
      public Byte Checksum; 
     } 

     [Serializable()] 
     public struct ModemStatus 
     { 
      public Byte Status; 
     } 

     public Byte[] TestSerialization() 
     { 
      Frame<ModemStatus> frame = new Frame<ModemStatus>(); 
      frame.StartDelimiter = 1; 
      frame.Lenght = 2; 
      frame.APIIdentifier = 3; 
      frame.FrameData.Status = 4; 
      frame.Checksum = 5; 

      BinaryFormatter formatter = new BinaryFormatter(); 
      MemoryStream stream = new MemoryStream(); 
      formatter.Serialize(stream, frame); 
      Byte[] buffer = stream.ToArray(); 
      return buffer; 
     } 
    } 
} 

Tôi có một diễn xuất Khung struct chung như là một wrapper cho nhiều loại tải trọng, để truyền nối tiếp. ModemStatus là một ví dụ về tải trọng như vậy.

Tuy nhiên, chạy TestSerialization() trả về bộ đệm byte dài (không có nội dung mong muốn)! Nó phải có chứa 6 byte. Có thể tuần tự hóa dữ liệu này một cách chính xác mà không cần sắp xếp theo cách thủ công không?

Trả lời

0

lẽ chung phương pháp Serialize/Deserialize:

public static string SerializeObject<T>(T obj) 
{ 
     string xmlString = null; 
     using(MemoryStream memoryStream = new MemoryStream()) 
     { 
     using(XmlSerializer xs = new XmlSerializer(typeof(T))) 
     { 
      XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8); 
      xs.Serialize(xmlTextWriter, obj); 
      memoryStream = (MemoryStream)xmlTextWriter.BaseStream; 
      xmlString = UTF8ByteArrayToString(memoryStream.ToArray());  
     } 
     } 
     return xmlString; 
} 

public static T DeserializeObject<T>(string xml) 
{ 
    XmlSerializer xs = new XmlSerializer(typeof(T)); 
    MemoryStream memoryStream = new MemoryStream(StringToUTF8ByteArray(xml)); 
    XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8); 
    return (T)xs.Deserialize(memoryStream); 
} 

Original tìm thấy here.

+0

Bạn có thể đã viết rằng trong nửa mã ... – leppie

+2

Xin lỗi để khai thác quá khứ nhưng mã này là khủng khiếp. XmlSerializer không phải là IDisposable nên không thể sử dụng câu lệnh.'MemoryStream() mới được tạo ra và xử lý, nhưng không bao giờ được sử dụng. 'memoryStream' được gán cho hai lần, nó sẽ không biên dịch vì là một phần của câu lệnh using. 'UTF8ByteArrayToString()' và 'StringToUTF8ByteArray()' chỉ không được định nghĩa ở bất kỳ đâu. Có lẽ bạn có thể dành thời gian để sửa nó ngay bây giờ bạn có nhiều kinh nghiệm hơn? –

8

Chris says, bạn có thể sử dụng mã không an toàn - trong trường hợp đó bạn nên chắc chắn rằng bạn chỉ định bố cục một cách rõ ràng. Tại thời điểm đó tất nhiên bạn đang giảm khả năng tối ưu hóa của CLR một chút - bạn sẽ kết thúc với truy cập chưa được ký, mất nguyên tử vv Điều đó có thể không phù hợp với bạn, nhưng nó đáng ghi nhớ.

Cá nhân, tôi coi đây là một cách khá dễ vỡ để sắp xếp/deserialize. Nếu bất cứ điều gì thay đổi, dữ liệu của bạn không thể đọc được. Nếu bạn cố gắng chạy trên một kiến ​​trúc sử dụng một endianness khác nhau, bạn sẽ tìm thấy tất cả các giá trị của bạn hơi say vv Ngoài ra, bằng cách sử dụng bố trí trong bộ nhớ sẽ thất bại ngay khi bạn cần sử dụng một loại tham chiếu - mà cũng có thể ảnh hưởng đến kiểu thiết kế của riêng bạn, khuyến khích bạn sử dụng các cấu trúc mà bạn sẽ sử dụng các lớp khác.

tôi xa thích hoặc đọc và ghi các giá trị một cách rõ ràng (ví dụ: với BinaryWriter, hoặc tốt hơn là a version of binary writer which lets you set the endianness) hoặc sử dụng một khuôn khổ serialization cầm tay như Protocol Buffers.

+0

Tôi đang cố gắng xác định cấu trúc giao thức cho một chip nhỏ chấp nhận lệnh thông qua UART (để thử nghiệm). Giao thức được đặt khá nhiều trong đá cho tôi và tôi sẽ không bao giờ lưu trữ dữ liệu và đọc lại sau. Tôi chắc chắn sẽ làm theo lời khuyên của bạn cho dịch vụ nghiêm túc. Cảm ơn! – joelr

1

Xem liên kết này. Điều này sử dụng cơ chế Marshal để lấy dữ liệu actaul của các cấu trúc của bạn và sao chép chúng vào một Byte []. Ngoài ra, làm thế nào để sao chép chúng trở lại. Những điều tốt đẹp về các chức năng này là họ mang tính tổng quát, vì vậy nó sẽ làm việc với tất cả các cấu trúc của bạn (trừ khi họ có các kiểu dữ liệu có kích thước biến như dây)

http://dooba.net/2009/07/c-sharp-and-serializing-byte-arrays/

+0

@David: Liên kết bị hỏng –

13

Chỉ cần sử dụng này hai phương pháp:

public static class StructTools 
{ 
    /// <summary> 
    /// converts byte[] to struct 
    /// </summary> 
    public static T RawDeserialize<T>(byte[] rawData, int position) 
    { 
     int rawsize = Marshal.SizeOf(typeof(T)); 
     if (rawsize > rawData.Length - position) 
      throw new ArgumentException("Not enough data to fill struct. Array length from position: "+(rawData.Length-position) + ", Struct length: "+rawsize); 
     IntPtr buffer = Marshal.AllocHGlobal(rawsize); 
     Marshal.Copy(rawData, position, buffer, rawsize); 
     T retobj = (T)Marshal.PtrToStructure(buffer, typeof(T)); 
     Marshal.FreeHGlobal(buffer); 
     return retobj; 
    } 

    /// <summary> 
    /// converts a struct to byte[] 
    /// </summary> 
    public static byte[] RawSerialize(object anything) 
    { 
     int rawSize = Marshal.SizeOf(anything); 
     IntPtr buffer = Marshal.AllocHGlobal(rawSize); 
     Marshal.StructureToPtr(anything, buffer, false); 
     byte[] rawDatas = new byte[rawSize]; 
     Marshal.Copy(buffer, rawDatas, 0, rawSize); 
     Marshal.FreeHGlobal(buffer); 
     return rawDatas; 
    } 
} 

và xác định cấu trúc của bạn như thế này (. xác định kích thước chính xác và gói (class) bởi một byte mặc định là 8):

[StructLayout(LayoutKind.Explicit, Size = 11, Pack = 1)] 
private struct MyStructType 
{ 
    [FieldOffset(0)] 
    public UInt16 Type; 
    [FieldOffset(2)] 
    public Byte DeviceNumber; 
    [FieldOffset(3)] 
    public UInt32 TableVersion; 
    [FieldOffset(7)] 
    public UInt32 SerialNumber; 
} 

Bây giờ bạn có thể sử dụng Deserialize

StructTools.RawDeserialize<MyStructType>(byteArray, 0); // 0 is offset in byte[] 

và serialize sử dụng

StructTools.RawSerialize(myStruct); 
+1

Tôi đã sử dụng câu trả lời này trong một tháng và nó khá tuyệt vời. – rocketsarefast

+0

Nó phải là awsome! Hãy xem ai đã viết câu trả lời thứ 2 ... Nhưng Jon vẫn là MacGyver của StackOverflow. – JCH2k

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