Serializing Arrays in C# - c#

Is there a way to Serialize an entire array in C# such as:
[Serializable()]
public class Data
{
public short Some_Short_Data = new short[100,100];
public string Some_String_Data = new string[100,100];
}
Then writting the file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
Data Write_Data = new Data();
XmlSerializer Writer = new XmlSerializer(typeof(Data));
using (FileStream file = File.OpenWrite("Myfile.xml"))
{
Writer.Serialize(file, Write_Data); //Writes data to the file
}
When I try this, it fails on:
XmlSerializer Writer = new XmlSerializer(typeof(Data));
Saying: "There was an error reflecting type 'Data' "
I am particularly new to both Stack and Xml/Xml Serialization so any help would be appreciated.

Multi-dimensional arrays are not supported by XmlSerialzier. But you can make a workaround by using a temp class something like this
public class Array100<T>
{
public T[] Data = new T[100];
}
public class Data
{
public Array100<short>[] Some_Short_Data = new Array100<short>[100];
public Array100<string>[] Some_String_Data = new Array100<string>[100];
}
BTW: No need for Serializable attribute. It is not used by XmlSerialzier

You cannot serialze a int[,] but you can serialize a int[][]. Before serializing your 2D array, just convert it into a jagged array like that :
var my2dArray = new int[2,5];
var myJaggedArray = new int [2][];
for(int i = 0 ; i < my2DArray.GetLength(0) ; i ++)
{
myJaggedArray[i] = new int[my2DArray.GetLength(1)];
for(int j = 0 ; j < my2DArray.GetLength(1) ; j ++)
myJaggedArray[i][j] = my2DArray[i,j];
}

I ran into this issue when attempting to serialize a multi-dimensional array that was one of the values in a dictionary of objects. The approach I took was to wrap all of the arrays in a class that is serializable before serialization and then unwrap them all afterwards.
If you don't care what the data looks like then you can serialize the object using the binary formatter, which can serialize anything marked as serializable, and then persist the binary data as a base 64 encoded string.
The wrapper implementation is below.
[Serializable]
public class UnserializableValueWrapper: IXmlSerializable
{
private static readonly BinaryFormatter Formatter = new BinaryFormatter();
public UnserializableValueWrapper([NotNull] object value)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
this.Value = value;
}
public UnserializableValueWrapper()
{
}
public object Value { get; private set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
reader.ReadStartElement();
var length = int.Parse(reader.GetAttribute("length"));
reader.ReadStartElement("Data");
var buffer = new byte[length];
reader.ReadContentAsBase64(buffer, 0, length);
using (var stream = new MemoryStream(buffer))
{
this.Value = Formatter.Deserialize(stream);
}
reader.ReadEndElement();
reader.ReadEndElement();
}
public void WriteXml(XmlWriter writer)
{
writer.WriteStartElement("Data");
using (var stream = new MemoryStream())
{
Formatter.Serialize(stream, this.Value);
var buffer = stream.ToArray();
writer.WriteAttributeString("length", buffer.Length.ToString());
writer.WriteBase64(buffer, 0, buffer.Length);
}
writer.WriteEndElement();
}
}

Related

C# XML Serialization Observable Collection

I'm trying to deserialize a bunch of data that was serialized by an old version of the code. When the data was serialized the classes had a different structure from the current class structure. To keep this data working in my new code, I hade to add the old classes structure to the code just for import this serialized data. I'm calling this classes as 'class'_oldVersions. To deserialize, I'm using this code:
className_oldVersions temp_className = new className_oldVersions();
XmlSerializer testSerializer = new XmlSerializer(typeof(className_oldVersions),
new XmlRootAttribute { ElementName = "className" });
temp_className = (ObservedData_OldVersions)testSerializer.Deserialize(ms_MemoryStream);
This code works fine, and I can deserialize the data using a diffent class name from the original. My problem is when I try to use this same procedure to deserialize an observable collection.
I created a code that reproduce my problem. In this code I serialize an observable collection of class OptimizationVariables and I would like to deserialize to an observable collection of class OptimizationVariablies_NewClass that has similar structure from the original one.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading.Tasks;
using System.Xml.Serialization;
using System.Collections.ObjectModel;
public class OptimizationVariables
{
public string VariableName { get; set; }
}
public class OptimizationVariables_NewClass
{
public string VariableName { get; set; }
}
public class ModelsCollection
{
private ModelsCollection()
{
}
private ObservableCollection<OptimizationVariables> m_optimizationVariables =
new ObservableCollection<OptimizationVariables>();
public ObservableCollection<OptimizationVariables> OptimizationVariables
{
get { return m_optimizationVariables; }
set { m_optimizationVariables = value; }
}
private ObservableCollection<OptimizationVariables_NewClass> m_optimizationVariables_NewClass =
new ObservableCollection<OptimizationVariables_NewClass>();
public ObservableCollection<OptimizationVariables_NewClass> OptimizationVariables_NewClass
{
get { return m_optimizationVariables_NewClass; }
set { m_optimizationVariables_NewClass = value; }
}
}
class Program
{
static void Main(string[] args)
{
//Here I serialize an ObservableCollection of 2 OptimizationVariables instances
Serialize();
//Here I deserialize for the same class and works fine
Deserialize();
//Here I try to deserialize to a new class with same structure, but different name. I a have an error.
Deserialize2NewClass();
}
static void Serialize()
{
MemoryStream ms;
ObservableCollection<OptimizationVariables> OptimizationVariables2Serialize = new ObservableCollection<OptimizationVariables>();
OptimizationVariables opt_var1 = new OptimizationVariables();
opt_var1.VariableName = "Variable Name 1";
OptimizationVariables2Serialize.Add(opt_var1);
OptimizationVariables opt_var2 = new OptimizationVariables();
opt_var1.VariableName = "Variable Name 2";
OptimizationVariables2Serialize.Add(opt_var1);
ms = new MemoryStream();
XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<OptimizationVariables>));
serializer.Serialize(ms, OptimizationVariables2Serialize);
TextWriter sw = new StreamWriter("XML_File_x64.bin");
sw.WriteLine(Convert.ToBase64String(ms.ToArray()));
sw.Close();
}
static void Deserialize()
{
byte[] memoryData;
MemoryStream ms;
TextReader sw = new StreamReader("XML_File_x64.bin");
memoryData = Convert.FromBase64String(sw.ReadLine());
ms = new MemoryStream(memoryData);
ObservableCollection<OptimizationVariables> OptimizationVariablesDeserialized = new ObservableCollection<OptimizationVariables>();
XmlSerializer deserializer = new XmlSerializer(typeof(ObservableCollection<OptimizationVariables>));
OptimizationVariablesDeserialized = (ObservableCollection<OptimizationVariables>)deserializer.Deserialize(ms);
Console.Write(OptimizationVariablesDeserialized.Count());
sw.Close();
}
static void Deserialize2NewClass()
{
byte[] memoryData;
MemoryStream ms;
TextReader sw = new StreamReader("XML_File_x64.bin");
memoryData = Convert.FromBase64String(sw.ReadLine());
ms = new MemoryStream(memoryData);
ObservableCollection<OptimizationVariables_NewClass> OptimizationVariablesDeserialized = new ObservableCollection<OptimizationVariables_NewClass>();
XmlSerializer deserializer = new XmlSerializer(typeof(ObservableCollection<OptimizationVariables_NewClass>));
OptimizationVariablesDeserialized = (ObservableCollection<OptimizationVariables_NewClass>)deserializer.Deserialize(ms);
Console.Write(OptimizationVariablesDeserialized.Count());
sw.Close();
}
}
The problem in your code is that the class OptimizationVariables name is saved in the xml data when serializing. but for deserialization it expects a OptimizationVariables_NewClass name which is not within the xml file.
consider the fact that not only the variable names, but also the class names will be used when serializing a class or struct...
So, just changed your code to make it save the data in readable xml file, then replacing "OptimizationVariables" with "OptimizationVariables_NewClass" solved the error:
using System;
using System.Linq;
using System.IO;
using System.Xml.Serialization;
using System.Collections.ObjectModel;
public class OptimizationVariables
{
public string VariableName { get; set; }
}
public class OptimizationVariables_NewClass
{
public string VariableName { get; set; }
}
public class ModelsCollection
{
private ModelsCollection()
{
}
private ObservableCollection<OptimizationVariables> m_optimizationVariables =
new ObservableCollection<OptimizationVariables>();
public ObservableCollection<OptimizationVariables> OptimizationVariables
{
get { return m_optimizationVariables; }
set { m_optimizationVariables = value; }
}
private ObservableCollection<OptimizationVariables_NewClass> m_optimizationVariables_NewClass =
new ObservableCollection<OptimizationVariables_NewClass>();
public ObservableCollection<OptimizationVariables_NewClass> OptimizationVariables_NewClass
{
get { return m_optimizationVariables_NewClass; }
set { m_optimizationVariables_NewClass = value; }
}
}
class Program
{
static void Main(string[] args)
{
Serialize();
Deserialize();
Deserialize2NewClass();
}
static void Serialize()
{
ObservableCollection<OptimizationVariables> OptimizationVariables2Serialize = new ObservableCollection<OptimizationVariables>();
OptimizationVariables opt_var1 = new OptimizationVariables();
opt_var1.VariableName = "Variable Name 1";
OptimizationVariables2Serialize.Add(opt_var1);
OptimizationVariables opt_var2 = new OptimizationVariables();
opt_var1.VariableName = "Variable Name 2";
OptimizationVariables2Serialize.Add(opt_var1);
TextWriter writer = new StreamWriter("XML_File.xml");
XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<OptimizationVariables>));
serializer.Serialize(writer, OptimizationVariables2Serialize);
writer.Close();
}
static void Deserialize()
{
TextReader sw = new StreamReader("XML_File.xml");
ObservableCollection<OptimizationVariables> OptimizationVariablesDeserialized = new ObservableCollection<OptimizationVariables>();
XmlSerializer deserializer = new XmlSerializer(typeof(ObservableCollection<OptimizationVariables>));
OptimizationVariablesDeserialized = (ObservableCollection<OptimizationVariables>)deserializer.Deserialize(sw);
Console.Write(OptimizationVariablesDeserialized.Count());
sw.Close();
}
static void Deserialize2NewClass()
{
TextReader sw = new StreamReader("XML_File.xml");
var str = sw.ReadToEnd();
sw.Close();
str = str.Replace("OptimizationVariables", "OptimizationVariables_NewClass");
var stream = new StringReader(str);
ObservableCollection<OptimizationVariables_NewClass> OptimizationVariablesDeserialized = new ObservableCollection<OptimizationVariables_NewClass>();
XmlSerializer deserializer = new XmlSerializer(typeof(ObservableCollection<OptimizationVariables_NewClass>));
OptimizationVariablesDeserialized = (ObservableCollection<OptimizationVariables_NewClass>)deserializer.Deserialize(stream);
Console.Write(OptimizationVariablesDeserialized.Count());
}
}
now it works fine!

Why can I no longer deserialize this data?

I am using the following code to serialize some data and save it to file:
MemoryStream stream = new MemoryStream();
DataContractJsonSerializer serializer = new DataContractJsonSerializer((typeof(Item)));
Item item = ((Item)list.SelectedItems[0].Tag);
serializer.WriteObject(stream, item);
var filepath = Program.appDataPath + list.SelectedItems[0].Group.Name + ".group";
stream.Position = 0;
using (FileStream fileStream = new FileStream(filepath, FileMode.Create))
{
stream.WriteTo(fileStream);
}
And later on, I'm trying to read back that data from file and insert it into ListView:
private void OpenFiles()
{
// DEBUG ONLY:
// Read into memorystream and filestream.
Console.WriteLine("Attempeting to open note.");
bool canLoad = false;
foreach (string file in Directory.GetFiles(Program.appDataPath))
{
if (file.EndsWith(".group"))
{
MemoryStream stream = new MemoryStream();
DataContractJsonSerializer serializer =
new DataContractJsonSerializer(
typeof(
List<Item>
)
);
using (FileStream fileStream =
new FileStream(
file,
FileMode.Open)
)
{
fileStream.CopyTo(stream);
}
stream.Position = 0;
//List<Withdrawal> tempWithList = new List<Withdrawal>();
foreach (Item item in (List<Item>)serializer.ReadObject(stream))
{
Console.WriteLine(item.Title + " " + item.Group.Name);
Item.Items.Add(item);
}
//Console.WriteLine("Got file \{file}");
//if (file.EndsWith(".group"))
//{
// Console.WriteLine("File is a group.");
// MemoryStream stream = new MemoryStream();
// DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(List<Item>));
// using (FileStream fileStream = new FileStream(file, FileMode.Open))
// {
// fileStream.CopyTo(stream);
// }
// Console.WriteLine("Got stream");
// stream.Position = 0;
// try
// {
// Item.Items = (List<Item>)serializer.ReadObject(stream);
// Console.WriteLine("WTF?");
// }
// catch(Exception exception)
// {
// Console.WriteLine(exception.Message);
// }
// Console.WriteLine(Item.Items.Count);
// canLoad = true;
//}
//else Console.WriteLine("File is not a group.");
}
if (canLoad)
{
//list.Items.Clear();
foreach (Item item in Item.Items)
{
ListViewGroup group = new ListViewGroup(item.Group.Name);
list.Groups.Add(group);
list.Items.Add(
new ListViewItem(
item.Title,
group)
);
Console.WriteLine(item.Title + " " + item.Group.Name);
}
}
}
}
Now, the above exact code works in an older program (few months old), but it's not working in a new program. I have no idea why. I have set breakpoints EVERYWHERE and it has proven to be kind of pointless in this case.
One thing I did learn from setting a breakpoint is that even though the stream contains the data expected, the very next second, when it gets added to list, it is NULL. There is nothing in the list. I've run out of ideas, and Google wasn't of much help.
Group.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
namespace Notes
{
[DataContract]
public class Group
{
[DataMember]
public string Name { get; set; }
}
}
Item.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
namespace Notes
{
[DataContract]
[Serializable]
public class Item : Note
{
[DataMember]
public static List<Item> Items = new List<Item>();
[DataContract]
public enum ItemType
{
Group,
Note
}
[DataMember]
public ItemType Type { get; set; }
[DataMember]
public int Index { get; set; }
}
}
Note.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
namespace Notes
{
[DataContract]
public class Note
{
[DataMember]
public string Title { get; set; }
[DataMember]
public string Content { get; set; }
[DataMember]
public Group Group;
[DataContract]
public enum NoteImportance
{
Important,
Neutral,
NotImportant
}
[DataMember]
public NoteImportance Importance { get; set; }
[DataMember]
public bool Protected { get; set; }
}
}
How can I deserialize these objects/read from file and get them into a List or ListView? I've already done this, but for some reason it's not working anymore.
Any help would be appreciated.
When you create a .group file, you serialize a single Item:
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Item));
// And later
serializer.WriteObject(stream, item);
But when you deserialize the contents of a .group file, you try to deserialize a List<Item>:
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(List<Item>));
// And later
foreach (Item item in (List<Item>)serializer.ReadObject(stream))
{
Item.Items.Add(item);
}
Those types don't match. But in order to deserialize the data you previously serialized, they need to match - or at least, the deserialized type cannot be a collection if the serialized type was, because collections are serialized as JSON arrays while other classes are serialized as JSON objects (name/value pairs).
Since it looks like each .group file has a single item, and there are many .group files in the directory you are scanning, you probably just want to do
var serializer = new DataContractJsonSerializer(typeof(Item));
// And later
var item = (Item)serializer.ReadObject(stream);
if (item != null)
Item.Items.Add(item);

C# XML Serialize object of Array object "There was an error generating the XML document."

I'm having a problem with the c# XML serialization system it's throws an Ambigus exception,
There was an error generating the XML document.
Now i have a Class that contains refferences to other classes and arrays of the other classes
E.G
namespace P2PFileLayout
{
public class p2pfile
{
public FileList FileList;
public StatusServer StatusServer;
public String Hash;
}
}
namespace P2PFileLayout.parts
{
public class StatusServer
{
public Auth Auth;
public Servers Servers;
}
public class Servers
{
public Server[] Server;
}
public class Server
{
public bool AuthRequired = false;
public string Address;
}
public class Files
{
public File[] File;
}
public class File
{
public string FileName = "";
public int BlockSize = 0;
public int BlockCount = 0;
}
public class Directory
{
public string Name;
public Files Files;
public Directory[] Dir;
}
public class Auth
{
public AuthServer[] AuthServer;
}
public class FileList
{
public Files Files;
public Directory[] Directory;
}
}
My Example data
// create the test file
testFile = new p2pfile();
// create a fake fileList
testFile.FileList = new P2PFileLayout.parts.FileList();
testFile.FileList.Directory = new P2PFileLayout.parts.Directory[1];
testFile.FileList.Directory[0] = new P2PFileLayout.parts.Directory();
testFile.FileList.Directory[0].Name = "testFolder";
testFile.FileList.Directory[0].Files = new P2PFileLayout.parts.Files();
testFile.FileList.Directory[0].Files.File = new P2PFileLayout.parts.File[2];
testFile.FileList.Directory[0].Files.File[0] = new P2PFileLayout.parts.File();
testFile.FileList.Directory[0].Files.File[0].FileName = "test.txt";
testFile.FileList.Directory[0].Files.File[0].BlockSize = 64;
testFile.FileList.Directory[0].Files.File[0].BlockCount = 1;
testFile.FileList.Directory[0].Files.File[1] = new P2PFileLayout.parts.File();
testFile.FileList.Directory[0].Files.File[1].FileName = "test2.txt";
testFile.FileList.Directory[0].Files.File[1].BlockSize = 64;
testFile.FileList.Directory[0].Files.File[1].BlockCount = 1;
// create a fake status server
testFile.StatusServer = new P2PFileLayout.parts.StatusServer();
testFile.StatusServer.Servers = new P2PFileLayout.parts.Servers();
testFile.StatusServer.Servers.Server = new P2PFileLayout.parts.Server[1];
testFile.StatusServer.Servers.Server[0] = new P2PFileLayout.parts.Server();
testFile.StatusServer.Servers.Server[0].Address = "http://localhost:8088/list.php";
// create a file hash (real)
HashGenerator.P2PHash hashGen = new HashGenerator.P2PHash();
testFile.Hash = hashGen.getHash();
treeView1.Nodes.Add(new TreeNode("Loading..."));
Classes.CreateTreeView ctv = new Classes.CreateTreeView();
ctv.BuildTreeView(testFile.FileList, treeView1);
treeView1.AfterCheck += new TreeViewEventHandler(treeView1_AfterCheck);
Now that is not as complicated as mine in terms of dept as my i loop objects so dir has support for more dirs but thats just an example
Then i'm serializing to a string var as i want to do a little more than just serialize it but here is my serialization
private string ToXml(object Obj, System.Type ObjType)
{
// instansiate the xml serializer object
XmlSerializer ser = new XmlSerializer(ObjType);
// create a memory stream for XMLTextWriter to use
MemoryStream memStream = new MemoryStream();
// create an XML writer using our memory stream
XmlTextWriter xmlWriter = new XmlTextWriter(memStream, Encoding.UTF8);
// write the object though the XML serializer method using the W3C namespaces
ser.Serialize(xmlWriter, Obj);
// close the XMLWriter
xmlWriter.Close();
// close the memoryStream
memStream.Close();
// get the string from the memory Stream
string xml = Encoding.UTF8.GetString(memStream.GetBuffer());
// remove the stuff before the xml code we care about
xml = xml.Substring(xml.IndexOf(Convert.ToChar(60)));
// clear any thing at the end of the elements we care about
xml = xml.Substring(0, (xml.LastIndexOf(Convert.ToChar(62)) + 1));
// return the XML string
return xml;
}
Can any one see why this is not working or any clues as to why it would not work normally
Why are you doing it manually?
What about this approach?
http://support.microsoft.com/kb/815813
Test test = new Test() { Test1 = "1", Test2 = "3" };
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(test.GetType());
MemoryStream ms = new MemoryStream();
x.Serialize(ms, test);
ms.Position = 0;
StreamReader sr = new StreamReader(ms);
string xml = sr.ReadToEnd();
As for the ambigous message, take a look into the inner exceptions. XML serialization errors use innerexception a lot, and you usually have to look to all levels of InnerExceptions to know what is really happening.

deserialize "long" string with protobuf for c# doesn't work properly for me

I'm obviously doing something basic wrong but I can't figure it out and don't find documentation.
I'm experimenting with proto-buf for .NET by Marc Gravell, and trying to serialize and deserialize objects. Once an object contains a string which is "too long" (didn't try to pinpoint the size threshold but it's few hundred bytes) the string doesn't deserialize properly for me.
This is my code:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using ProtoBuf;
namespace ConsoleApplication1
{
public class Program
{
[ProtoContract]
public class test
{
[ProtoMember(1)]
public int i;
[ProtoMember(2)]
public string s1;
[ProtoMember(3)]
public string s2;
[ProtoMember(4)]
public char[] arrchars;
[ProtoMember(5)]
public Dictionary<int, string> Dict = new Dictionary<int, string>();
}
static void Main(string[] args)
{
test var1 = new test();
var1.i = 10;
var1.s1 = "Hello";
var1.arrchars = new char[] {'A', 'B', 'C'};
var1.Dict.Add(10, "ten");
var1.Dict.Add(5, "five");
var1.s2 = new String('X', 520);
string s = PBSerializer.Serialize(typeof (test), var1);
test var2 = null;
PBSerializer.Deserialize(s, out var2);
}
public static class PBSerializer
{
public static string Serialize(Type objType, object obj)
{
MemoryStream stream = new MemoryStream();
ProtoBuf.Serializer.Serialize(stream, obj);
// ProtoBuf.Serializer.SerializeWithLengthPrefix(stream, obj, PrefixStyle.Fixed32, 1);
stream.Flush();
stream.Position = 0;
StreamReader sr = new StreamReader(stream);
string res = sr.ReadToEnd();
stream.Dispose();
sr.Dispose();
return res;
}
public static void Deserialize(string serializedObj, out test obj)
{
MemoryStream stream = new MemoryStream(Encoding.ASCII.GetBytes(serializedObj));
obj = ProtoBuf.Serializer.Deserialize<test>(stream);
// obj = ProtoBuf.Serializer.DeserializeWithLengthPrefix<test>(stream, PrefixStyle.Fixed32, 1);
stream.Dispose();
}
}
}
}
var2.s2 is not identical to var1.s2 - it has an extra character at the beginning of the string and truncates the majority of the end of the string. If however, I change the length of var1.s2 to a small number (say 52 instead of 520 chars) my problem goes away but I need to be able to serialize long strings.
I assume it has to do with something I do wrong with the setting the PrefixStyle (?) or perhaps I'm not using the right encoding (?). However, trial and error didn't help me sort it out.
I'm using .NET 3.5 and tried it with versions 444 & 450 with the same result.
Thanks.
You're serializing binary data - but then trying to read it as if it were text. It's not - so don't do that.
If you have to turn arbitrary binary data into text, use Convert.ToBase64String and Convert.FromBase64String.
public static class PBSerializer
{
public static string Serialize(Type objType, object obj)
{
using (MemoryStream stream = new MemoryStream())
{
ProtoBuf.Serializer.Serialize(stream, obj);
return Convert.ToBase64String(stream.ToArray());
}
}
// Ideally change this to use a return value instead of an out parameter...
public static void Deserialize(string serializedObj, out test obj)
{
byte[] data = Convert.FromBase64String(serializedObj);
using (MemoryStream stream = new MemoryStream(data))
{
obj = ProtoBuf.Serializer.Deserialize<test>(stream);
}
}

Transmitting complex objects using TCP

I have a client server application in which I need to transmit a user defined object from Client to Server using TCP connection. My object is of the following structure:
class Conversation
{
private string convName, convOwner;
public ArrayList convUsers;
public string getConvName()
{
return this.convName;
}
public string getConvOwner()
{
return this.convOwner;
}
}
Please help me how to transmit this object at from client and again de-serialize it into appropriate object at server side.
As answered, you should make your object serializable. Once you did that with the Serializable attribute, you can use the famous BinaryFormatter to convert your object into a byte array.
You can find many examples out there for using the BinaryFormatter, just use your favorite search engine. Here's a short example:
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
public class SerializationUtils
{
public static byte[] SerializeToByteArray(object request)
{
byte[] result;
BinaryFormatter serializer = new BinaryFormatter();
using (MemoryStream memStream = new MemoryStream())
{
serializer.Serialize(memStream, request);
result = memStream.GetBuffer();
}
return result;
}
public static T DeserializeFromByteArray<T>(byte[] buffer)
{
BinaryFormatter deserializer = new BinaryFormatter();
using (MemoryStream memStream = new MemoryStream(buffer))
{
object newobj = deserializer.Deserialize(memStream);
return (T)newobj;
}
}
}
As for your class, it includes two private fields. I can't see where you set values for them, so I changed your code a bit, so that they can be set in the constructor. In addition, I added the needed Serializable attribute:
using System;
using System.Collections;
[Serializable]
public class Conversation
{
public Conversation(string convName, string convOwner)
{
this.convName = convName;
this.convOwner = convOwner;
}
public Conversation()
{
}
private string convName, convOwner;
public ArrayList convUsers;
public string getConvName()
{
return this.convName;
}
public string getConvOwner()
{
return this.convOwner;
}
}
Now let's put it all together, and see your class serialized and then deserialized, in a Console Application:
using System;
using System.Collections;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace Capishi
{
[Serializable]
public class Conversation
{
public Conversation(string convName, string convOwner)
{
this.convName = convName;
this.convOwner = convOwner;
}
public Conversation()
{
}
private string convName, convOwner;
public ArrayList convUsers;
public string getConvName()
{
return this.convName;
}
public string getConvOwner()
{
return this.convOwner;
}
}
public class SerializationUtils
{
public static byte[] SerializeToByteArray(object request)
{
byte[] result;
BinaryFormatter serializer = new BinaryFormatter();
using (MemoryStream memStream = new MemoryStream())
{
serializer.Serialize(memStream, request);
result = memStream.GetBuffer();
}
return result;
}
public static T DeserializeFromByteArray<T>(byte[] buffer)
{
BinaryFormatter deserializer = new BinaryFormatter();
using (MemoryStream memStream = new MemoryStream(buffer))
{
object newobj = deserializer.Deserialize(memStream);
return (T)newobj;
}
}
}
class Program
{
static void Main(string[] args)
{
// create and initialize a conversation object
var convName = "Capishi";
var convOwner = "Ice Cream";
Conversation myConversation = new Conversation(convName, convOwner);
myConversation.convUsers = new ArrayList();
myConversation.convUsers.Add("Ron Klein");
myConversation.convUsers.Add("Rakesh K");
// serialize to a byte array
byte[] data = SerializationUtils.SerializeToByteArray(myConversation);
// print the resulting byte array if you want
// PrintArray(data);
// deserialize the object (on the other side of the communication
Conversation otherConversation = SerializationUtils.DeserializeFromByteArray<Conversation>(data);
// let's see if all of the members are really there
Console.WriteLine("*** start output ***");
Console.WriteLine("otherConversation.getConvName() = " + otherConversation.getConvName());
Console.WriteLine("otherConversation.getConvOwner() = " + otherConversation.getConvOwner());
Console.WriteLine("otherConversation.convUsers:");
foreach (object item in otherConversation.convUsers)
{
Console.WriteLine(item);
}
Console.WriteLine("*** done output ***");
// wait before close
Console.ReadLine();
}
/// <summary>
/// just a helper function to dump an array to the console's output
/// </summary>
/// <param name="data"></param>
private static void PrintArray(byte[] data)
{
for (int i = 0; i < data.Length; i++)
{
Console.Write("{0:000}", data[i]);
if (i < data.Length - 1)
Console.Write(", ");
}
Console.WriteLine();
}
}
}
The result is:
*** start output ***
otherConversation.getConvName() = Capishi
otherConversation.getConvOwner() = Ice Cream
otherConversation.convUsers:
Ron Klein
Rakesh K
*** done output ***
And a final note:
I'd use the generic List instead of the outdated ArrayList, unless you're bound to .NET 1.*.
One good course of action is to expose this object as a DataContract to a framework like WCF, and use the appropriate transports available in that framework.
For example:
[DataContract]
class Conversation
{
private string convName, convOwner;
public ArrayList convUsers;
[DataMember]
public string ConvName
{
get { return this.convName; }
}
[DataMember]
public string ConvOwner
{
get { return this.convOwner; }
}
}
You need to make your object serializable.

Categories

Resources