none
Сериализация структуры из массива RRS feed

  • Вопрос

  • Помогите пж..

    есть структура

    [Serializable]
    public struct DiskProperties
    {
    public Int16 length; //длина телеграммы
    public float[] usedSpace; //количество занятого места (Гб)
    public float[] percentUsedSpace; //% занятого места в (%)
    }

    Необходимо ее перевести в массив байтов, чтобы передать по TCP\IP.

    Эти примеры работают, когда в структуре нет массива.

    Если их использовать, то в массиве байтов лежать только первые значения массивов, остальное нули.

    http://habrahabr.ru/post/114953/

    public static byte[] RawSerialize(object anything)
    {
    int rawsize = Marshal.SizeOf(anything);
    IntPtr buffer = Marshal.AllocHGlobal(rawsize);
    Marshal.StructureToPtr(anything, buffer, false);
    byte[] rawdata = new byte[rawsize];
    Marshal.Copy(buffer, rawdata, 0, rawsize);
    Marshal.FreeHGlobal(buffer);
    return rawdata;
    }

    и еще один пример от туда же

    public static byte[] RawSerialize(object anything)
    {
    int rawsize = Marshal.SizeOf(anything);
    byte[] rawdata = new byte[rawsize];
    GCHandle handle = GCHandle.Alloc(rawdata, GCHandleType.Pinned);
    Marshal.StructureToPtr(anything, handle.AddrOfPinnedObject(), false);
    handle.Free();
    return rawdata;
    }

    • Перемещено YatajgaEditor 29 октября 2013 г. 6:09
    29 октября 2013 г. 5:57

Ответы

  • Сериализацию .NET в данном случае использовать бессмысленно, т.к. принимающая сторона ничего не знает о Ваших типах (структура она или еще что-то). Поэтому оперировать нужно "сырыми" данными (байтами). Протокол передачи следующий: размер первого массива, первый массив, размер второго массива, второй массив. Вот как-то так:

    void SerializeOne(Stream _out, dynamic value)
    {
    	byte[] data = BitConverter.GetBytes(value);
    	_out.Write(data, 0, data.Length);
    }
    
    public byte[] Serialize()
    {
    	using (MemoryStream ms = new MemoryStream())
    	{
    		// размер первого массива
    		SerializeOne(ms, usedSpace.Length);
    
    		// первый массив
    		foreach (float f in usedSpace)
    		{
    			SerializeOne(ms, f);
    		}
    
    		// размер второго массива
    		SerializeOne(ms, percentUsedSpace.Length);
    
    		// второй массив
    		foreach (float f in percentUsedSpace)
    		{
    			SerializeOne(ms, f);
    		}
    
    		return ms.ToArray();
    	}
    }

    Общий размер данных в "телеге" в этом случае можно не передавать.


    30 октября 2013 г. 8:17
  • предлагаю решение, отправлять данные так

    не профи. не судите строго

    interface IUnnamed
        {
            byte[] GetBytesFromString(string s);
            string GetStringFromBytes(byte[] b);
            string Pack(DiskProperties obj);
            DiskProperties Unpack(string s);
        }
        public class DiskProperties:IUnnamed
        {
            public float[] D_Space;
            public float[] D_Space_Percent;
    
            public byte[] GetBytesFromString(string s)
            {
                return ASCIIEncoding.UTF8.GetBytes(s);
            }
            public string Pack(DiskProperties temp)
            {
                char delimeter_space = '-';
                char delimeter_space_Percent = '_';
                char info_delim = '|';
                string res = string.Empty;
                foreach (var item in temp.D_Space)
                {
                    res = res + item.ToString()+delimeter_space;
                }
                res = res + info_delim;
                foreach (var item in temp.D_Space_Percent)
                {
                    res = res + item.ToString() + delimeter_space_Percent;
                }
                return res;
            }
            public DiskProperties Unpack(string s)
            {
                DiskProperties res = new DiskProperties();
                List<float> res_Temp = new List<float>();
                char delimeter_space = '-';
                char delimeter_space_Percent = '_';
                char info_delim = '|';
                string[] temp = s.Split(info_delim);
                string t1 = temp[0];
                string t2 = temp[1];
                string[] tt1 = t1.Split(delimeter_space);
                string[] tt2 = t2.Split(delimeter_space_Percent);
                foreach (var item in tt1)
                {
                    res_Temp.Add(float.Parse(item));
                }
                res.D_Space = res_Temp.ToArray();
                res_Temp.Clear();
                foreach (var item in tt2)
                {
                    res_Temp.Add(float.Parse(item));
                }
                res.D_Space_Percent = res_Temp.ToArray();
                res_Temp.Clear();
                return res;
            }
            public string GetStringFromBytes(byte[] b)
            {
                return ASCIIEncoding.UTF8.GetString(b);
            }
        }

    класс>строка>байты

    никакой потери. + можно шифровать

    30 октября 2013 г. 10:34

Все ответы

  • Нужно отнаследовать класс от интерфейса ISerializable и самостоятельно реализовать сериализацию класса
    29 октября 2013 г. 6:33
  • public static byte[] Serialize(object obj){
        using(MemoryStream ms=new MemoryStream()){
            new BinaryFormatter().Serialize(ms,obj);
            return ms.ToArray();
        }
    }
    public static object Deserialize(byte[] data){
        using(MemoryStream ms=new MemoryStream(data,false)){
            return new BinaryFormatter().Deserialize(ms);
        }
    }
    29 октября 2013 г. 8:41
  • Нужно отнаследовать класс от интерфейса ISerializable и самостоятельно реализовать сериализацию класса
    Не совсем понятно. Если бы примерчик :)
    29 октября 2013 г. 9:36
  • public static byte[] Serialize(object obj){
        using(MemoryStream ms=new MemoryStream()){
            new BinaryFormatter().Serialize(ms,obj);
            return ms.ToArray();
        }
    }
    public static object Deserialize(byte[] data){
        using(MemoryStream ms=new MemoryStream(data,false)){
            return new BinaryFormatter().Deserialize(ms);
        }
    }
    Спасибо за труды, но сериализация проходит криво. Размер и наполнение структуры на входе и на выходе - массива байт, разные.
    29 октября 2013 г. 9:38
  • То есть, даже если Вы делаете сериализацию, а потом сразу десериализацию, то получаете объект с другим содержимым?
    object obj=...
    
    object clone=Deserialize(Serialize(obj));
    29 октября 2013 г. 9:53
  • То есть, даже если Вы делаете сериализацию, а потом сразу десериализацию, то получаете объект с другим содержимым?
    object obj=...
    
    object clone=Deserialize(Serialize(obj));
    Проблема в том, что я отправляю массив байтов по TCP\IP, а принимающая сторона принимает его на языке C++ и десериализацию не делает, а раскладывает массив байтов по полочкам.
    30 октября 2013 г. 5:33
  • При передаче массива вначале всегда передают его длину, чтобы принимающая сторона знала, сколько памяти нужно выделить и сколько байтов затем читать. Другими словами, должен быть определен протокол приема-передачи.
    30 октября 2013 г. 5:42
  • При передаче массива вначале всегда передают его длину, чтобы принимающая сторона знала, сколько памяти нужно выделить и сколько байтов затем читать. Другими словами, должен быть определен протокол приема-передачи.
    Поэтому и создана структура, в которой первая переменная отвечает за длину телеги
    public struct DiskProperties
    {
    public Int16 length; //длина телеграммы
    public float[] usedSpace; //количество занятого места (Гб)
    public float[] percentUsedSpace; //% занятого места в (%)
    }
    30 октября 2013 г. 6:26
  • Сериализацию .NET в данном случае использовать бессмысленно, т.к. принимающая сторона ничего не знает о Ваших типах (структура она или еще что-то). Поэтому оперировать нужно "сырыми" данными (байтами). Протокол передачи следующий: размер первого массива, первый массив, размер второго массива, второй массив. Вот как-то так:

    void SerializeOne(Stream _out, dynamic value)
    {
    	byte[] data = BitConverter.GetBytes(value);
    	_out.Write(data, 0, data.Length);
    }
    
    public byte[] Serialize()
    {
    	using (MemoryStream ms = new MemoryStream())
    	{
    		// размер первого массива
    		SerializeOne(ms, usedSpace.Length);
    
    		// первый массив
    		foreach (float f in usedSpace)
    		{
    			SerializeOne(ms, f);
    		}
    
    		// размер второго массива
    		SerializeOne(ms, percentUsedSpace.Length);
    
    		// второй массив
    		foreach (float f in percentUsedSpace)
    		{
    			SerializeOne(ms, f);
    		}
    
    		return ms.ToArray();
    	}
    }

    Общий размер данных в "телеге" в этом случае можно не передавать.


    30 октября 2013 г. 8:17
  • Ещё, наверное, стоит добавить обработку случая, когда порядок байт на отправляющей и принимающей системе различается.
    30 октября 2013 г. 8:33
  • Да, это важно. Но такие вещи просто должны обговариваться в протоколе.
    30 октября 2013 г. 8:36
  • Можете использовать двойную сериализацию (хотя это не двойная получается). Сериализуете структуру в XML, а XML -> в двоичные данные. Потом обратный процесс. Накладных расходов получается больше, но тогда уже ничего не теряется и не нарушается.

    Сделаем содержимое сообщества лучше, вместе!

    30 октября 2013 г. 9:32
    Модератор
  • предлагаю решение, отправлять данные так

    не профи. не судите строго

    interface IUnnamed
        {
            byte[] GetBytesFromString(string s);
            string GetStringFromBytes(byte[] b);
            string Pack(DiskProperties obj);
            DiskProperties Unpack(string s);
        }
        public class DiskProperties:IUnnamed
        {
            public float[] D_Space;
            public float[] D_Space_Percent;
    
            public byte[] GetBytesFromString(string s)
            {
                return ASCIIEncoding.UTF8.GetBytes(s);
            }
            public string Pack(DiskProperties temp)
            {
                char delimeter_space = '-';
                char delimeter_space_Percent = '_';
                char info_delim = '|';
                string res = string.Empty;
                foreach (var item in temp.D_Space)
                {
                    res = res + item.ToString()+delimeter_space;
                }
                res = res + info_delim;
                foreach (var item in temp.D_Space_Percent)
                {
                    res = res + item.ToString() + delimeter_space_Percent;
                }
                return res;
            }
            public DiskProperties Unpack(string s)
            {
                DiskProperties res = new DiskProperties();
                List<float> res_Temp = new List<float>();
                char delimeter_space = '-';
                char delimeter_space_Percent = '_';
                char info_delim = '|';
                string[] temp = s.Split(info_delim);
                string t1 = temp[0];
                string t2 = temp[1];
                string[] tt1 = t1.Split(delimeter_space);
                string[] tt2 = t2.Split(delimeter_space_Percent);
                foreach (var item in tt1)
                {
                    res_Temp.Add(float.Parse(item));
                }
                res.D_Space = res_Temp.ToArray();
                res_Temp.Clear();
                foreach (var item in tt2)
                {
                    res_Temp.Add(float.Parse(item));
                }
                res.D_Space_Percent = res_Temp.ToArray();
                res_Temp.Clear();
                return res;
            }
            public string GetStringFromBytes(byte[] b)
            {
                return ASCIIEncoding.UTF8.GetString(b);
            }
        }

    класс>строка>байты

    никакой потери. + можно шифровать

    30 октября 2013 г. 10:34