Protocol buffer serializing with inheritance. Derived classes are empty - c#

I faced with strange problem. After reading a lot, I tried to deploy protobuf-net in my app. Classes binds with simple inheritance. After presumably successful serialization, I am trying to deserialize to list, but protobuf filling only base class!
here a bit of code:
serializing function
private static void serialize<T>(T obj) where T: Log
{
using (var fileStream =
new FileStream(fileName, FileMode.Append))
{
Serializer.SerializeWithLengthPrefix(fileStream, obj,
PrefixStyle.Base128, SerializeTypesDictionary.First(x =>
x.Value == obj.GetType()).Key);
}
}
deserialize
private static ArrayList deserialize(string filename)
{
object obj;
var arr = new ArrayList();
using (var fileStream = new FileStream(file, FileMode.Open))
{
while (Serializer.NonGeneric.TryDeserializeWithLengthPrefix
(fileStream, PrefixStyle.Base128, resolver, out obj))
{
arr.Add(obj);
}
}
return arr;
}
and classes
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)
[ProtoInclude(100, typeof(Error))]
[ProtoInclude(101, typeof(Record))]
[ProtoInclude(102, typeof(SqlQuery))]
public class Log
{...}
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class Error:Log
{...}
and other with same structure
So, what am I doing wrong?
P.S. after deserializing list consists right classes (not base! all classes deserialised in their types), but with empty fields, and full fields in base class.

The question closed.
Sorry for wasting yours time, if you wasted it=) Simply forgot attributes on one of the derived classes, and somehow its affect to others.

Related

Binary serialization without serializable attribute

I want to serailize my object and used BinaryFormatter class.
public static byte[] BinarySerialize(IMessage message)
{
using (var stream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, message);
return stream.ToArray();
}
}
But when I run the code, throws an exception.
SerializationException: Object is not marked as serializable.
I think this exception thrown by BinaryFormatter.
I do not want to mark as [Serializable] my objects. Or my library users may forget mark as [Serializable] their own Messages.
Is there any other way to binary serialize my objects without using [Serializable] attribute?
Since [Serializable] attribute cannot be added runtime, there are nooptions if you want to stick to the .Net built in Serialization.
You can
Use ISerializable interface in IMessage so that users has to implement Serialization in their implementations
Use an external library such as: http://sharpserializer.codeplex.com/ And by the way, they have moved to GitHub. See: https://github.com/polenter/SharpSerializer
public static byte[] BinarySerialize(IMessage message)
{
using (var stream = new MemoryStream())
{
var serializer = new SharpSerializer(true);
serializer.Serialize(message, stream );
return stream.ToArray();
}
}
Use JSON serialization
In addition to the other answers regarding 3rd party libs, depending on your needs you may choose to use XmlSerializer. (Better yet use a JSON serializer that doesn't require the SerializeableAttribute.)
These serializers do not require [Serializeable]. However, the XmlSerializer doesn't allow serialization of interfaces either. If you are good with concrete types it works. Compare serialization options.
E.G.
void Main()
{
var serialized = Test.BinarySerialize(new SomeImpl(11,"Hello Wurld"));
}
public class Test
{
public static string BinarySerialize(SomeImpl message)
{
using (var stream = new StringWriter())
{
var formatter = new XmlSerializer(typeof(SomeImpl));
formatter.Serialize(stream, message);
return stream.ToString().Dump();
}
}
}
public class SomeImpl
{
public int MyProperty { get;set;}
public string MyString { get;set; }
public SomeImpl() {}
public SomeImpl(int myProperty, String myString)
{
MyProperty = myProperty;
MyString = myString;
}
}
To avoid Net4x built in Serialization that require the [Serializable] attribute, use Newtonsoft.Json or System.Text.Json in netcore 3.1+ or Net5
string json= JsonConvert.SerializeObject(message);
//or System.Text.Json in netcore 3.1+
string json= System.Text.Json. JsonSerializer.Serialize(message);

Saving a Dictionary<int, object> in C# - Serialization?

I have a dictionary in c#
private Dictionary<int, UserSessionInfo> userSessionLookupTable = new Dictionary<int, UserSessionInfo>();
Now I have created a dictionary object
this.userSessionLookupTable.Add(userSessionInfoLogin.SessionId, userSessionInfoLogin);
Now I want a generic method to serialize and de-serialize the dictionory to byte array.
Like
public static void Serialize(Dictionary<int, object> dictionary, Stream stream)
{
//Code here
}
and
public static static Dictionary<int, object> Deserialize(Stream stream)
{
//Code here
}
Can anyone help me on this??
Try this....
public static void Serialize<Object>(Object dictionary, Stream stream)
{
try // try to serialize the collection to a file
{
using (stream)
{
// create BinaryFormatter
BinaryFormatter bin = new BinaryFormatter();
// serialize the collection (EmployeeList1) to file (stream)
bin.Serialize(stream, dictionary);
}
}
catch (IOException)
{
}
}
public static Object Deserialize<Object>(Stream stream) where Object : new()
{
Object ret = CreateInstance<Object>();
try
{
using (stream)
{
// create BinaryFormatter
BinaryFormatter bin = new BinaryFormatter();
// deserialize the collection (Employee) from file (stream)
ret = (Object)bin.Deserialize(stream);
}
}
catch (IOException)
{
}
return ret;
}
// function to create instance of T
public static Object CreateInstance<Object>() where Object : new()
{
return (Object)Activator.CreateInstance(typeof(Object));
}
Usage...
Serialize(userSessionLookupTable, File.Open("data.bin", FileMode.Create));
Dictionary<int, UserSessionInfo> deserializeObject = Deserialize<Dictionary<int, UserSessionInfo>>(File.Open("data.bin", FileMode.Open));
I have used 'Object' in the code above to fulfil your requirements but personally I would use 'T' which usually denotes a generic object in C#
You could use a MemoryStream for this purpose unless you don't want to create a file. But for the serializing method, if you don't want to return a value you should probably mark the stream parameter as [Out].
public static void Serialize(Dictionary<int, object> dictionary, [Out] Stream stream)
{
stream = new MemoryStream();
new BinaryFormatter().Serialize(stream, dictionary);
}
public static Dictionary<int, object> Deserialize(Stream stream)
{
return (Dictionary<int, object>)new BinaryFormatter().Deserialize(stream);
}
This will perform binary serialization.
EDIT:
To get it as a byte array you can just cast the stream returned from the Serialize() method to a MemoryStream, and then call .ToArray() on it.
This is an example:
MemoryStream outputStream;
Serialize(your dictionary here, outputStream);
byte[] bytes = outputStream.ToArray();
A bit late to answer maybe but here is a practical and more general answer from my experience: Use simple JSON serialisation if you want something quick and easy or if there is not much demand for data size or performance. If you want something highly space efficient, fast and cross-platform as well, use Google Protocol Buffers.
JSON
string output = JsonConvert.SerializeObject(myObject);
var copyOfMyObject = JsonConvert.DeserializeObject<MyObject>(output);
There are a lot of ways you can control the serialisation and use streams and so on.
Json.Net also supports dictionaries too.
Protobuf
There are two ways of using protobuf in c#: Using .proto message definitions and generate code or reflection based approach using your class definitions and attributes. It depends on your project but I prefer the message definitions personally.
Note that you might have to do your own munging before and after serialisation and deserialisation to use with a Dictionary.
Message definition based: using proto3
.proto file:
message Person {
int32 id = 1;
string name = 2;
}
Generate code using Google Protobuf:
protoc --csharp_out=$DST_DIR $SRC_DIR/person.proto
Serialise / deserialise using generated classes:
Person john = ...;
john.WriteTo(stream);
var copyOfJohn = Person.Parser.ParseFrom(stream);
Also, a quick note to let you know that there is probably a less known feature of proto3 is the capability of using JSON.
Reflection based: Protobuf-net
Here is a quick example (copied from protobuf-net page)
[ProtoContract]
class Person {
[ProtoMember(1)]
public int Id {get;set;}
[ProtoMember(2)]
public string Name {get;set;}
}
Serializer.Serialize(file, person);
newPerson = Serializer.Deserialize<Person>(file);
Binary, text, streams, oh my!
Apologies, I sort of ignored the question about 'Binary' serialisation (and was not a specific BinaryFormatter question) since there are lots of ways you can use streams to deal with data flowing in and out of a process. C# stream, reader and writer and other System.IO APIs are quite well documented.
For Binary Serialization
For more info take a look at BinaryFormatter.
Here is a possible solution:
public void Serialize(Dictionary<int, UserSessionInfo> dictionary, Stream stream)
{
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(dictionary.Count);
foreach (var obj in dictionary)
{
writer.Write(obj.Key);
writer.Write(obj.Value);
}
writer.Flush();
}
public Dictionary<int, UserSessionInfo> Deserialize(Stream stream)
{
BinaryReader reader = new BinaryReader(stream);
int count = reader.ReadInt32();
var dictionary = new Dictionary<int, UserSessionInfo>(count);
for (int n = 0; n < count; n++)
{
var key = reader.ReadInt32();
var value = reader.ReadString();
dictionary.Add(key, value);
}
return dictionary;
}
but you still need to have UserSessionInfo ToString() converter;
For XML Serialization
Create a sample class Session
public class Session
{
[XmlAttribute]
public int SessionID;
[XmlAttribute]
public UserSessionInfo SessionInfo;
}
Then you can create XmlSerializer if you want to serialize it as XML
XmlSerializer serializer = new XmlSerializer(
typeof(Session[]),
new XmlRootAttribute() { ElementName = "sessions" }
);
And now you can serialize or deserialize.
Serialization:
serializer.Serialize(
stream,
dict.Select(kv => new Session(){SessionID = kv.Key, SessionInfo = kv.Info}).ToArray()
);
Deserialization:
var deserialized = (
(Session[])serializer.Deserialize(stream)
).ToDictionary(i => i.id, i => i.info);
But you need to have ToString() method in your UserSessionInfo to store it in the XML.
And the XML may look like this:
<sessions>
<session id='int_goes_here' value='string_goes_here'/>
</sessions>
Hope this helps.
unless you want to build your own completely custom serializer then you need to use the built in serialization structure
The dictionary and all the referenced types must implement the inbuilt serialisation structure which requires classes to be decorated witht the correct attributes ie [Serializable], or [DataContact] and as object can't implement it then you can't serialise it directly, you need to change it to a serializable type

How to serialize IEnumerable<char> as string & Confusing Deserialization error

Lets say I have the following simple class:
[XmlRoot]
[XmlType("string")]
public partial class eString : IEnumerable<char>
{
string AsString {get;set;}
public IEnumerator<char> GetEnumerator()
{
return this.AsString.ToCharArray().ToList().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.AsString.ToCharArray().GetEnumerator();
}
public void Add(char character)
{
this.AsString += character;
}
}
Also a namedValue class:
[XmlRoot]
public partial class eNamedValue: KeyValuePair<eString,object>
{...}
And finally,
[XmlRoot]
public partial class eList<T>: List<eNamedValue>
{...}
Now, I serialize eList or any class that inherits from eList using the following XML serializer:
public static XDocument Serialize<T>(this T obj) where T: new()
{
Type tt = obj.GetType();
XmlSerializer xsSubmit = new XmlSerializer(tt);
StringWriter sww = new StringWriter();
XmlWriter writer = XmlWriter.Create(sww);
xsSubmit.Serialize(writer, obj);
return XDocument.Parse(sww.ToString());
}
Serialization works - but my eString is being serialized as a character array, so instead of getting "string", I get individual characters values:
<eNamedList>
<Key>99</Key>
<Key>111</Key>
<Key>100</Key>
<Key>101</Key>...
Also, on Deserialization (SOLVED, see Update#1 below):
public static T Deserialize<T>(this XDocument xml) where T: new()
{
var serializer = new XmlSerializer(typeof(T));
T result;
using (TextReader reader = new StringReader(xml.ToString()))
{
result = (T)serializer.Deserialize(reader);
}
return result;
}
I receive the following error:
System.Runtime.Serialization.SerializationException: Error in line 1 position 111. Expecting element 'eNamedList' from namespace 'http://schemas.datacontract.org/2004/07/Epic'.. Encountered 'Element' with name 'eNamedList', namespace ''.
So, my questions become:
How do I control serialization of my eString, or any IEnumerable<char> and always serialize as a string?
Why, when the element names match, do I get a failure on deserialization? (Am I just missing a namespace declaration?)
Thanks!
Update #1:
So, I removed the IEnumerable<char> interface from my eString, and just left the IEnumerable<char>.GetEnumerator() method, which allows my string to be used AS an IEnumerable<char> while in a foreach loop, but serializes as a string. #WIN
Also, thanks to dbc, updated the original post with the XML Deserializer (rather than DataContract Serializer) and deserialization works.
To answer your questions:
You probably shouldn't reinvent the wheel with custom string solutions. Regardless, if you want an (insanely) high-level of control over your (de-)serialization, you could implement IXmlSerializable and do the exact same thing yourself.
[XmlRoot("string-wrapper")]
public class CustomString : IXmlSerializable
{
public string Value { get; set; }
public XmlSchema GetSchema()
{
return null; // GetSchema should not be used.
}
public void ReadXml(XmlReader reader)
{
reader.MoveToContent();
bool isEmpty = reader.IsEmptyElement;
reader.ReadStartElement();
if (!isEmpty)
{
Value = reader.ReadString();
reader.ReadEndElement();
}
}
public void WriteXml(XmlWriter writer)
{
writer.WriteString(Value);
}
}
Serialization of a CustomString now yields <string-wrapper>Testing</string-wrapper>. I'll post some test code at the end of this post.
Your deserialization is likely broken because the XmlSerializer doesn't know that the IEnumerable you marked serializable should actually be treated like a string.
And now... Forget what I just told you. Unless you have very specific formatting requirements you should not implement your own version of a string. The built-in formatter knows a lot more formatting tricks (http://www.w3.org/TR/xmlschema-2/#built-in-datatypes), so you should probably let it do it's job.
Serializing classes is where the attributes come in, though I recommend you switch to the WFC data contracts, which may sound scary at first but actually provides a whole lot more for a lot less code. Again, I'm not sure what you're trying to accomplish, but trust me you don't want to get into the habit of hand writing XML.
If you're up for it you might like dynamic objects and the ExpandoObject (http://www.codeproject.com/Tips/227139/Converting-XML-to-an-dynamic-object-using-ExpandoO). These eliminate types all together and allow you to create dictionaries, arrays, named properties, whatever, all on the fly!
Finally, easy on the generics! Deserializing generic classes is not a trivial task. Besides, you probably don't need to. If you want your own collections, try the System.Collections.ObjectModel namespace. You don't have to worry about maintaining lists and implementing interfaces, just what you're actually storing:
class DictionaryOfStringsAndObjects : KeyedCollection<string, object {...}
class CollectionOfStrings : Collection<string> {...}
Also, try to avoid partial classes unless an ORM or a large legacy class forces it on you. You shouldn't actually use them unless you're made to.
All the best, and to a future devoid of XML!
public class CustomSerializer
{
public static void Test()
{
var obj = new CustomString {Value = "Random string!"};
var serializer = new CustomSerializer();
var xml = serializer.Serialize(obj);
Console.WriteLine(xml);
var obj2 = serializer.Deserialize<CustomString>(xml);
}
public string Serialize(object obj)
{
var serializer = new XmlSerializer(obj.GetType());
using (var io = new StringWriter())
{
serializer.Serialize(io, obj);
return io.ToString();
}
}
public T Deserialize<T>(string xml)
{
var serializer = new XmlSerializer(typeof (T));
using (var io = new StringReader(xml))
{
return (T)serializer.Deserialize(io);
}
}
}

XMLSerialization: The type of the argument object 'Sw' is not primitive

I'm trying to serialize an object to an XML file, but am getting the above error.
The problem seems to be with objects that contain a list of a base class but is populated by objects derived from the base class.
Example code is as follows:
public class myObject
{
public myObject()
{
this.list.Add(new Sw());
}
public List<Units> list = new List<Units>();
}
public class Units
{
public Units()
{
}
}
public class Sw : Units
{
public Sw();
{
}
public void main()
{
myObject myObject = new myObject();
XmlSerializer serializer = new XmlSerializer(typeof(myObject));
TextWriter textWriter = new StreamWriter ("file.xml");
serializer.Serialize (textWriter, myObject);
}
E.g. an object that contains only a List<Units> which is populated by derived objects which inherit from the Units class (Sw).
Sorry for not providing my actual code but the objects involved are quite complex and this seems to be the only part of the object which wont successfully be serialized - and only when the list contains the derived classes.
How can I correctly serialize a class like this?
Mark Units class with XmlInclude attribute passing your derived class as parameter:
[XmlInclude(typeof(Sw))]
public class Units
{
public Units()
{
}
}
To Serialize an object to XML. you can use the following code
public String SerializeResponse(SW sw)
{
try
{
String XmlizedString = null;
XmlSerializer xs = new XmlSerializer(typeof(SW));
//create an instance of the MemoryStream class since we intend to keep the XML string
//in memory instead of saving it to a file.
MemoryStream memoryStream = new MemoryStream();
//XmlTextWriter - fast, non-cached, forward-only way of generating streams or files
//containing XML data
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
//Serialize emp in the xmlTextWriter
xs.Serialize(xmlTextWriter, sw);
//Get the BaseStream of the xmlTextWriter in the Memory Stream
memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
//Convert to array
XmlizedString = UTF8ByteArrayToString(memoryStream.ToArray());
return XmlizedString;
}
catch (Exception ex)
{
throw;
}
}
The method will return an XML String and to make the above function you need to import the following libraries:
using System.Xml;
using System.Xml.Serialization;
using System.IO;

How to serialize multiple members of "this"

I have a class with a couple of members I want to serialise to store state.
However, I want to serialise from WITHIN the class itself, not via some external class feeding it to a formatter.
So in theory I want to do something like:
[DataContract]
class MyClass
{
[DataMember]
private MyCompoundClass _someCompoundField;
[DataMember]
private int _someOtherField;
private void SaveState()
{
using (Stream stream = GetStream())
{
DataContractSerializer serialiser = new DataContractSerializer(typeof(MyClass));
serialiser.WriteObject(stream, this);
}
}
private void LoadState()
{
using (Stream stream = GetStream())
{
DataContractSerializer serialiser = new DataContractSerializer(typeof(MyClass));
this = (MyClass)serialiser.ReadObject(stream);
}
}
}
Now obviously the line
this = (MyClass)serialiser.ReadObject(stream);
is nonsense, but you can see what I'm trying to do. I want to serialise the two fields of my class from within the class. (I am using the WCF serializer, but I assume this will be the same if I use XmlSerializer).
I tried to implement this properly by serialising each field myself like so:
private void SaveState()
{
using (Stream stream = GetStream())
{
//serialise field 1
DataContractSerializer serialiser = new DataContractSerializer(typeof(MyCompoundClass));
serialiser.WriteObject(stream, _someCompoundField);
//serialise field 2
serialiser = new DataContractSerializer(typeof(int));
serialiser.WriteObject(stream, _someOtherField);
}
}
Now this works as a save, but when I come to read the document back in it throws an exception since there are two root nodes in the XML file.
How do I create my "wrapper" node to wrap my fields. Or is there some other way I should be doing this?
Many thanks,
I haven't seen any built-in deserialisation methods which modify an existing object, rather than returning a new one. Your options as I see them are:
Deserialise a new MyClass and copy the members over
Make LoadState static and have it return the deserialised MyClass
Use a different serialisation mechanism which can do what you want
you could at your LoadState do:
private void LoadState()
{
using (Stream stream = GetStream())
{
DataContractSerializer serialiser = new DataContractSerializer(typeof(MyClass));
MyClass deserialized = (MyClass)serialiser.ReadObject(stream);
this._someCompoundField = deserialized._someCompoundField;
this._someOtherField = deserialized._someOtherField;
}
}
Are you tied to a specific serializer? protobuf-net supports that use-case, for example:
[DataContract]
class MyClass
{
[DataMember(Order = 1)]
private MyCompoundClass _someCompoundField;
[DataMember(Order = 2)]
private int _someOtherField;
private void SaveState()
{
using (Stream stream = GetStream())
{
ProtoBuf.Serializer.Serialize(stream, this);
}
}
private void LoadState()
{
using (Stream stream = GetStream())
{
ProtoBuf.Serializer.Merge(stream, this);
}
}
}
Note the addition of Order = n on the member-attributes; that is because protobuf uses numeric identifiers on fields/properties, and needs a way to choose them. You can also use the project-specific [ProtoContract]/[ProtoMember(n)] attributes, but it works with the WCF ones too (as shown).
(Merge is also available on the non-generic 2.0 API - but you pass this in as an argument to Deseroalize instead)
Put your deserialization method in a static method:
class MyClass
{
public static MyClass LoadState()
{
// Deserialize, and return the new MyClass instance.
}
}
To serialize just some objects, annotate some fields to avoid serialization, or create a superclass or interface with just the fields in it you want to serialize.

Categories