How to deserialize class with array of serializable object - c#

I have class with array of some serialized class (named it elements). I want to serialize this class and then deserialize this. But deserialization is difficult.
Because I don't know type of element of my array before serialized this create two arrays match to elements array. In one (typeOfElements) keep type of element and in other (serializedElemnt) keep serialized string of elements. But I don't know after deserialized how to create my main elements array. How can I convert type to class to create my main array?
[ProtoContract]
class MyClass
{
.
.
public MyClass()
{
}
object[] elements;
[ProtoMember(1)]
string[] SerilizedElements;
[ProtoMember(2)]
string[] TypeOfElements;
[ProtoBeforeSerialization]
void initBeforeSerilize()
{
TypeOfElements = new string[elements.Length];
SerilizedElements = new string[elements.Length];
for (int i = 0; i < elements.Length; i++)
{
TypeOfElements[i] = elements[i].GetType().ToString();
using (MemoryStream ms = new MemoryStream())
{
Serializer.Serialize(ms, elements[i]);
SerilizedElements[i] = Encoding.UTF8.GetString(ms.ToArray());
}
}
}
[ProtoAfterDeserialization]
void initAfterSerilize()
{
for (int i = 0; i < SerilizedElements.Length; i++)
{
Type t = Type.GetType(TypeOfElements[i]);
using(MemoryStream ms=new MemoryStream(Encoding.ASCII.GetBytes(SerilizedElements[i])))
{
//I don't know how to write this line
elements[i]=Serializer.Deserialize<t>(ms);
}
}
}
}

For the "I only know the type at runtime" issue, look at Serializer.NonGeneric, which has all the methods you would want for working with a Type. The non-generic API is also the primary API on the v2 API, aka TypeModel. The string encoding issue has already been noted; if you need strings, base-64 should be used, but personally I'd use a byte[]. I would also suggest thinking about whether inheritance can be used instead of unknown types - this is certainly possible if the number of candidate types is finite and known.

Related

Generics and Types and Arrays

Can a 'generic type' be an array?
And in the cases where it is, how can one access that array?
Can you access a given generic type T as an array, when it is one, and as a non-array when it is not one?
For instance:
If I had a method like ->
void MustBeOfType<T>(T value){}
Can I have ->
MustBeOfType<int>(10);
And Also ->
MustBeOfType<int[]>( new int[] { 1, 2, 3 } );
And within those generic methods, can they access the values as one would expect? - one as an int and one as an int[]?
I think there might be something with typeof(T).IsArray()... but I just can't for the life of me figure out how to cast the parameter as an array when it is one.
Thanks!
You could...but, I'm not sure you should:
void MustBeOfType<T>(T value)
{
Array array = value as Array;
if (array != null) //It is an array
{
foreach (var arrayItem in array)
{
}
for (int i = 0; i < array.Length; i++)
{
var arrayItem = array.GetValue(i);
}
}
else //It is not an array
{
}
}
I am not sure what you are trying to do but Generic types can be anything. You can put restrictions to your generic type with where clause, but this is up to you and up to functionality and context.
Lets take List as example. Let say that we have List inside List. Then we define it as:
List<List<string>> myListOfList = new List<List<string>>();
your must be of type can also be anything ( if you didnt put restriction with where clause)
MustBeOfType<int[][]>()
MustBeOfType<List<List<string>>>()
MustBeOfType<AnyOtherGenericClass<List<string>>>()
and to be able to access it:
class MustBeOfType<T>
{
private T _value;
MustBeofType(T value)
{
_value = value;
}
}
to be able to make operation on , you can use reflection or if you put where restriction and your where restriction has Type, then you can see properties of that Type.

How can i define a class to deserialze binary file

I would like to know how can i define a class for a binary file which contains objects with float array of size 19.
please see the attached picture of how data looks like in Hex Editor Neo. when displayed as float
i have tried following but no luck. please tell what i am doing wrong here.
[ProtoContract]
public class ChannelData
{
[ProtoMember(1)]
public List<float> array = new List<float>(19);
}
using (var file = File.OpenRead("0.cnl"))
{
ChannelData newchannel = Serializer.Deserialize<ChannelData>(file);
}
I am using protobuff for deserialize. Thankyou!
that binary file is not serialized using any binary serializer its raw file.
so i think it can not be deserialize using any deserializer? may be need to do with BitConverter but have no idea..
You can load floats to the list, then split it to Channels, like
public static unsafe List<float> LoadToList(byte[] bytes)
{
var list = new List<float>();
var step = sizeof (float);
for (int i = 0; i < bytes.Length; i += step)
{
fixed (byte* pbyte = &bytes[i])
{
list.Add(*((float*)pbyte));
}
}
return list;
}

How to determine the type of an generic array element with reflection?

What I'm trying to do is without generic types is simple.
SomeType[] s = new SomeType[5];
for(int i = 0; i < 5: i++){
s[i]= new SomeType();
}
Using generics this is what I have so far.
private static T TypeMapper<T>(dynamic handle) {
if (typeof(T).IsArray)
{
Type elementType = typeof(T).GetElementType();
int arrayLength = handle.Length;
Array array = Array.CreateInstance(elementType, arrayLength);
//??? How to get the type of an generic array element
Type typeOfthis = typeof(array[0]).GetElementType();
for (int i = 0; i < arrayLength; i++)
{
//??? How to create an instance from it.
array[i] = new typeOfthis ();
}
T obj = (T)(object)array;
return obj;
}
else
{}
Then calling the TypeMapper function with.
dynamic t = new TextSpan[4];
var result = TypeMapper<TextSpan[]>(t)
How can I get the type of an generic array element.
Type typeOfthis = typeof(array[0]).GetElementType();//Not Working
And how to create an instance from it.
array[i] = new typeOfthis ();
Your help is highly appreciated.
The Solution of Rubidium 37 was marked as correct
Type typeofitem = array.GetType().GetElementType();
instead of
Type typeOfthis = typeof(array[0]).GetElementType();
and
array.SetValue(Activator.CreateInstance(typeofitem), i);
instead of
array[i] = new typeOfthis ();
Thanks also for the other solutions here, but they quite missing the point that I can pass SomeType[] or SomeType as T to TypeMapper<T>(dynamic handle).
Then I can then derive from the type if it is an array or not and process the handle as needed.
offtopic
The long breath reason behind it is to marshal javascript types from v8 used by the v8dotnet wrapper which returns objects which have an equal behavior like dynamic objects.Instead of explaining what an InternalHandle handle is I reduced it to dynamic so that it is comprehensible.
When you have an array element, you have an object, that can be a different type from other array elements (like a derived type).
Then you can get the type of a single element array as you do for any standalone object.
You should replace
Type typeOfthis = typeof(array[0]).GetElementType();
with
Type typeOfthis = array[0].GetType();
Whereas, when you want create an object of any type, you can simply using Activator.CreateInstance or retrieve one ConstructorInfo from the Type.
Replace
array[i] = new typeOfthis ();
with
array[i] = Activator.CreateInstance(typeOfthis);
Just for you yo know..
- Remember that Activator.CreateInstance will try to call a parameterless constructor of the type you pass to it, then that type need to have such a constructor.
- The object you create need to be compatible with the array's Element type (assignable to it, like a derived class).
- If you know in advance that all elements of the array are (or should be) of the same type, then you don't need to get the type for each element; instead, typeof(T).GetElementType() should be enough.
- If the element type is a class and you don't need to assign concrete values because they are assigned later, or you know that the elementi type is struct, then you don't need to fill the array.
A part of that details, if you want to create a new array, given its length and the (concrete) type of its elements, you can try one of the following:
public static Array CreateAndFillArray(Type type, int length)
{
var result = Array.CreateInstance(type, length);
for (int ixItem = 0; ixItem < result.Length; ixItem++)
result.SetValue(Activator.CreateInstance(type), ixItem);
return result;
}
public static T[] CreateAndFillArray<T>(int length)
{
var type = typeof(T);
var result = new T[length];
for (int ixItem = 0; ixItem < result.Length; ixItem++)
result[ixItem] = (T)Activator.CreateInstance(type);
return result;
}
public static T[] CreateAndFillArray<T>(int length)
where T : new()
{
var result = new T[length];
for (int ixItem = 0; ixItem < result.Length; ixItem++)
result[ixItem] = new T();
return result;
}
Regards,
Daniele.
You need to learn something more about generics (and generalization in general). If you want to fill an array with default instances of a type, you can use this:
void ConstructAll<T>(T[] array) where T : new()
{
for (var i = 0; i < array.Length; i++)
{
array[i] = new T();
}
}
However, this doesn't really look like a good idea. What problem are you trying to solve? And why would you think using dynamic is a good solution?

Binary Deserializing implementations of an abstract class

I have an abstract class that I'm trying to serialize and deserialize the concrete implementations of. In my abstract base class I have this:
[DataContract]
public class MyAbstractBase
{
[DataMember]
public string Foo { get; set; }
// some other abstract methods that derived classes have to implement
}
And to that class I add a method to serialize:
public string SerializeBase64()
{
// Serialize to a base 64 string
byte[] bytes;
long length = 0;
MemoryStream ws = new MemoryStream();
DataContractSerializer serializer = new DataContractSerializer(this.GetType());
XmlDictionaryWriter binaryDictionaryWriter = XmlDictionaryWriter.CreateBinaryWriter(ws);
serializer.WriteObject(binaryDictionaryWriter, this);
binaryDictionaryWriter.Flush();
length = ws.Length;
bytes = ws.GetBuffer();
string encodedData = bytes.Length + ":" + Convert.ToBase64String(bytes, 0, bytes.Length, Base64FormattingOptions.None);
return encodedData;
}
This seems to work fine, in that it produces "something" and doesn't actually throw any errors.
Of course, the problem comes with deserialization. I added this:
public static MyAbstractBase DeserializeBase64(string s)
{
int p = s.IndexOf(':');
int length = Convert.ToInt32(s.Substring(0, p));
// Extract data from the base 64 string!
byte[] memorydata = Convert.FromBase64String(s.Substring(p + 1));
MemoryStream rs = new MemoryStream(memorydata, 0, length);
DataContractSerializer serializer = new DataContractSerializer(typeof(MyAbstractBase ), new List<Type>() { typeof(SomeOtherClass.MyDerivedClass) });
XmlDictionaryReader binaryDictionaryReader = XmlDictionaryReader.CreateBinaryReader(rs, XmlDictionaryReaderQuotas.Max);
return (MyAbstractBase)serializer.ReadObject(binaryDictionaryReader);
}
I thought by adding the "known types" to my DataContractSerializer, it would be able to figure out how to deserialize the derived class, but it appears that it doesn't. It complains with the error:
Expecting element 'MyAbstractBase' from namespace 'http://schemas.datacontract.org/2004/07/MyApp.Foo'.. Encountered 'Element' with name 'SomeOtherClass.MyDerivedClass', namespace 'http://schemas.datacontract.org/2004/07/MyApp.Foo.Bar'.
So any idea what I'm missing here?
I put together a simple demonstration of the problem on a dot net fiddle here:
http://dotnetfiddle.net/W7GCOw
Unfortunately, it won't run directly there because it doesn't include the System.Runtime.Serialization assemblies. But if you drop it into a Visual Studio project, it will serialize fine, but balks at deserialization.
When serializing your data, use the same overloaded method for serialization as you use for deserialization:
DataContractSerializer serializer = new DataContractSerializer(typeof(MyAbstractBase ), new List<Type>() { typeof(SomeOtherClass.MyDerivedClass) });
Also, Declare a KnownType attribute around your base class so it knows what possible derived classes it may deserialize:
[DataContract]
[KnownType(typeof(SomeOtherclass.MyDerivedClass))]
public class MyAbstractBase
{
[DataMember]
public string Foo { get; set; }
// some other abstract methods that derived classes have to implement
}
So I identified a couple of problems. The first is that the CreateBinaryWriter just doesn't seem to work at all. So I dropped it and just directly serialized with serializer.WriteObject(ws,this);
The second problem is on serialization I did this:
DataContractSerializer serializer = new DataContractSerializer(this.GetType(), new List<Type>() { typeof(SomeOtherClass.MyDerivedClass) });
The problem is that the type I'm passing there isn't the base type, it's whatever type I'm calling this function from. But in deserialization I have this:
DataContractSerializer serializer = new DataContractSerializer(typeof(MyAbstractBase ), new List<Type>() { typeof(SomeOtherClass.MyDerivedClass) });
This isn't the same serializer. The type is different. So changing both to typeof(MyAbstractBase) fixed the problem in my simple example.
Of course, in my real project I'm still getting errors with it complaining on deserialization with The data at the root level is invalid. Line 1, position 1. which is odd because I've compared both the data coming out of serialization and the data going into deserialization and they are absolutely identical. No rogue BOM or anything.
EDIT: I've solved the data at the root level is invalid problem. It seems that decorating my [DataContract] attributes with explicit Name and Namespace properties solved the problem and, as an added bonus, reduced the size of my data because I greatly shortened the namespaces. Quite why the serializer couldn't cope with the real namespaces I don't know.
Another Edit: One last wrinkle. I think the namespace thing was a red herring and I got it working only by pure coincidence. The root of the problem (along with the solution) is explained here:
https://msmvps.com/blogs/peterritchie/archive/2009/04/29/datacontractserializer-readobject-is-easily-confused.aspx
When you do GetBuffer() on the MemoryStream you can get excess null characters because of the way the underlying array resizes itself as needed. This puts a bunch of nulls on the end of your serialization (which can be spotted as a bunch of As after base 64'ing the array) and these are what are screwing up the deserialization with the very confusing The data at the root level is invalid. Line 1, position 1.. It's confusing because the problem isn't at the beginning at all, it's at the END!!!
In case anybody is interested, by serialization now looks like this:
public string SerializeBase64()
{
// Serialize to a base 64 string
byte[] bytes;
long length = 0;
using (MemoryStream ws = new MemoryStream())
{
XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(ws);
DataContractSerializer serializer = new DataContractSerializer(typeof(MyAbstractBase ), new List<Type>() { typeof(SomeOtherClass.MyDerivedClass) });
serializer.WriteObject(writer, this);
writer.Flush();
length = ws.Length;
// Note: https://msmvps.com/blogs/peterritchie/archive/2009/04/29/datacontractserializer-readobject-is-easily-confused.aspx
// We need to trim nulls from the buffer produced by the serializer because it'll barf on them when it tries to deserialize.
bytes = new byte[ws.Length];
Array.Copy(ws.GetBuffer(), bytes, bytes.Length);
}
string encodedData = bytes.Length + ":" + Convert.ToBase64String(bytes, 0, bytes.Length, Base64FormattingOptions.None);
return encodedData;
}

Converting byte[] to an object

I'm trying to convert an object which I have in a byte[] to an object.
I've tried using this code I found online:
object byteArrayToObject(byte[] bytes)
{
try
{
MemoryStream ms = new MemoryStream(bytes);
BinaryFormatter bf = new BinaryFormatter();
//ms.Position = 0;
return bf.Deserialize(ms,null);
}
catch
{
return null;
}
}
SerializationException: "End of Stream encountered before parsing was
completed.".
I've tried it with the ms.Position = 0 line uncommented of course too...
bytes[] is only 8 bytes long, each byte isn't null.
Suggestions?
[edit]
The byte[] was written to a binary file from a c++ program using something along the lines of
void WriteToFile (std::ostream& file,T* value)
{
file.write(reinterpret_cast<char*>(value), sizeof(*T))
}
Where value may be a number of different types.
I can cast to some objects okay from the file using BitConverter, but anything BitConverter doesn't cover I can't do..
As was stated by cdhowie, you will need to manually deserialize the encoded data. Based on the limited information available, you may either want an array of objects or an object containing an array. It looks like you have a single long but there is no way to know from your code. You will need to recreate your object in its true form so take the below myLong as a simple example for a single long array. Since it was unspecified I'll assume you want a struct containing an array like:
public struct myLong {
public long[] value;
}
You could do the same thing with an array of structs, or classes with minor changes to the code posted below.
Your method will be something like this: (written in the editor)
private myLong byteArrayToObject(byte[] bytes) {
try
{
int len = sizeof(long);
myLong data = new myLong();
data.value = new long[bytes.Length / len];
int byteindex = 0;
for (int i = 0; i < data.value.Length; i++) {
data.value[i] = BitConverter.ToInt64(bytes,byteindex);
byteindex += len;
}
return data;
}
catch
{
return null;
}
}

Categories

Resources