What mistake am I making when serializing? - c#

This leads to serialization exception at runtime. It's just a demo project to test the best way to do this. I included the main method and the class which im trying to serialize.
Ignore: I really cant add more detail, i've described the problem, attached the code, this "please add more details" thing is the stupidest thing ever. Let me post it already.
Data toSend = new Data();
toSend.Output();
///SERIALIZE
BinaryFormatter formatter = new BinaryFormatter();
Stream streamOut = File.OpenWrite("file");
formatter.Serialize(streamOut, toSend);
streamOut.Close();
Console.WriteLine("----------------------------");
///DESERIALIZE
Stream streamIn = File.OpenRead("file");
Object received = formatter.Deserialize(streamIn);
Data toReceive = (Data)received;
toReceive.Output();
class Data : ISerializable
{
int integerData;
string stringData;
bool booleanData;
int shouldnotbeserialized;
public Data()
{
integerData = 1;
stringData = "Hello";
booleanData = true;
shouldnotbeserialized = 55;
}
//To deserialize
public Data(SerializationInfo info, StreamingContext context)
{
integerData = info.GetInt32("theint");
stringData = info.GetString("thestring");
booleanData = info.GetBoolean("thebool");
}
public void Output()
{
Console.WriteLine(integerData);
Console.WriteLine(stringData);
Console.WriteLine(booleanData);
Console.WriteLine(shouldnotbeserialized);
}
//Implemented method to serialize
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("thestring", stringData);
info.AddValue("theint", integerData);
info.AddValue("thebool", booleanData);
}
}
Exception message:
Type 'SerializationDemo.Data' in Assembly 'SerializationDemo,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked
as serializable.

The answer is given to you in the exception message:
Type 'SerializationDemo.Data' in Assembly 'SerializationDemo,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked
as serializable.
You need to mark your class with the Serializable attribute.
[Serializable()]
class Data : ISerializable
From the context it seems like you are going to be transmitting the object down a network (deduced due to the local variable names toSend, toReceive). You need to be aware that, if you where for example, using a client-server model, an object serialized and sent from the server software will not be deserializable from the client software by default.
This is because the fundamental characteristic of binary serialization is that it preserves type system fidelity. Type system fidelity denotes that type information is not lost in the serialization process. This means that when you serialize an object, is is not only the "object's state" written to the data stream, but also the name of the class and the containing assembly. Even if you defined the class Data in the assembly that is going to deserialize the data, the deserialize method will fail (throw an exception) because the deserializer will be looking for the type Program1.Namespace.Data not Program2.Namespace.Data. To remedy this you can define your data messages in a mutual assembly (class library) or by defining a SerializationBinder that will allow you to basically trick the deserialize method into thinking you are still in the same assembly.

Related

Modify property serialization/deserialization in WinForms Form1.resx file

I have a custom class and made the same serialization as System.Drawing.Image, by implementing ISerializable interface and two methods:
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) to give the serializer my data in the format I need.
A constructor in my class: protected MyClass(SerializationInfo info, StreamingContext context)
[Serializable]
[TypeConverter(typeof(MyClassTypeConverter))]
[Editor(typeof(MyClassEditor), typeof(UITypeEditor))]
public class MyClass : MarshalByRefObject, ISerializable, ICloneable, IDisposable
A have added my class as a property to a custom button -> MyButton.MyClass
The problem is that at design time when I set MyClass the serializer writes the assembly name and version in the RESX file of the form before my custom serialized object. Here is how it looks like:
<assembly alias="MyAssembly" name="MyAssembly, Version=2020.1.1, Culture=neutral, PublicKeyToken=XXX" />
<data name="myButton1.MyClass" type="MyAssembly.MyClass, MyAssembly">
<value>Some long text data</data>
</data>
After a new release my clients started having this exception:
System.InvalidCastException: '[A]MyAssembly.MyClass cannot be cast to [B]MyAssembly.MyClass. Type A originates from 'MyAssembly, Version=2020.1.1, Culture=neutral, PublicKeyToken=XXX' in the context 'Default' at location 'C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\...
Here is the designer file:
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form2));
this.myButton1 = new MyAssembly.MyButton();
this.SuspendLayout();
//
// myButton1
//
this.myButton1.Location = new System.Drawing.Point(5, 5);
this.myButton1.Name = "myButton1";
this.myButton1.Size = new System.Drawing.Size(200, 25);
this.myButton1.MyClass = ((MyAssembly.MyClass)(resources.GetObject("myButton1.MyClass")));
this.myButton1.TabIndex = 0;
this.myButton1.Text = "myButton1";
The assembly where type MyClass is located is already loaded, because MyButton is located in the same assembly. So I do not need the RESX file to contain information about the assembly.
Currently I cannot find a solution on my own and I am thinking of two options:
How can I prevent serializing the <assembly alias="MyAssembly"...> in the resx file of the form?
Can I load MyClass from the resx file without loading the described assembly?

how come BinaryFormatter can serialize an Action<> but Json.net cannot

Trying to serialize/deserialize an Action<>.
Try #1 naive by me
JsonConvert.SerializeObject(myAction);
...
JsonConvert.Deserialize<Action>(json);
Deserialize fails saying it cannot serialize Action.
Try #2
JsonConvert.DeserializeObject<Action>(ctx.SerializedJob, new JsonSerializerSettings {ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor });
Same(ish) failure.
Try # 3
Then I found http://mikehadlow.blogspot.com/2011/04/serializing-continuations.html
This uses BinaryFormatter. I dropped this in (base64 encoding the binary to a string). Worked perfectly first time.
Try #4
I then found
https://github.com/DevrexLabs/Modules.JsonNetFormatter
Which is an IFormatter module for json.net. Wired that in, same failure - cannot deserialize.
So how come BinaryFormatter can do it but Json.net cannot?
EDIT:
The general reply is - "thats the most stupid thing to want to do". Let me show what I am trying to do
MyJobSystem.AddJob(ctx=>
{
// code to do
// ......
}, DateTime.UtcNow + TimeSpan.FromDays(2));
Ie - execute this lambda in 2 days time.
This works fine for me. Using BinaryFormatter. I was curious about why one serializing infrastructure could do it but the other could not. They both seem to have the same rules about what can and cannot be processed
The reason that BinaryFormatter is (sometimes) able to round-trip an Action<T> is that such delegates are marked as [Serializable] and implement ISerializable.
However, just because the delegate itself is marked as serializable doesn't mean that its members can be serialized successfully. In testing, I was able to serialize the following delegate:
Action<int> a1 = (a) => Console.WriteLine(a);
But attempting to serialize the following threw a SerializationException:
int i = 0;
Action<int> a2 = (a) => i = i + a;
The captured variable i apparently is placed in a non-serializable compiler-generated class thereby preventing binary serialization of the delegate from succeeding.
On the other hand, Json.NET is unable to round-trip an Action<T> despite supporting ISerializable because it does not provide support for serialization proxies configured via SerializationInfo.SetType(Type). We can confirm that Action<T> is using this mechanism with the following code:
var iSerializable = a1 as ISerializable;
if (iSerializable != null)
{
var info = new SerializationInfo(a1.GetType(), new FormatterConverter());
var initialFullTypeName = info.FullTypeName;
iSerializable.GetObjectData(info, new StreamingContext(StreamingContextStates.All));
Console.WriteLine("Initial FullTypeName = \"{0}\", final FullTypeName = \"{1}\".", initialFullTypeName, info.FullTypeName);
var enumerator = info.GetEnumerator();
while (enumerator.MoveNext())
{
Console.WriteLine(" Name = {0}, objectType = {1}, value = {2}.", enumerator.Name, enumerator.ObjectType, enumerator.Value);
}
}
When run, it outputs:
Initial FullTypeName = "System.Action`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]", final FullTypeName = "System.DelegateSerializationHolder".
Name = Delegate, objectType = System.DelegateSerializationHolder+DelegateEntry, value = System.DelegateSerializationHolder+DelegateEntry.
Name = method0, objectType = System.Reflection.RuntimeMethodInfo, value = Void <Test>b__0(Int32).
Notice that FullTypeName has changed to System.DelegateSerializationHolder? That's the proxy, and it's not supported by Json.NET.
This begs the question, just what is written out when a delegate is serialized? To determine this we can configure Json.NET to serialize Action<T> similarly to how BinaryFormatter would by setting
DefaultContractResolver.IgnoreSerializableAttribute = false
DefaultContractResolver.IgnoreSerializableInterface = false
JsonSerializerSettings.TypeNameHandling = TypeNameHandling.All
If I serialize a1 using these settings:
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
ContractResolver = new DefaultContractResolver
{
IgnoreSerializableInterface = false,
IgnoreSerializableAttribute = false,
},
Formatting = Formatting.Indented,
};
var json = JsonConvert.SerializeObject(a1, settings);
Console.WriteLine(json);
Then the following JSON is generated:
{
"$type": "System.Action`1[[System.Int32, mscorlib]], mscorlib",
"Delegate": {
"$type": "System.DelegateSerializationHolder+DelegateEntry, mscorlib",
"type": "System.Action`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]",
"assembly": "mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"target": null,
"targetTypeAssembly": "Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"targetTypeName": "Question49138328.TestClass",
"methodName": "<Test>b__0",
"delegateEntry": null
},
"method0": {
"$type": "System.Reflection.RuntimeMethodInfo, mscorlib",
"Name": "<Test>b__0",
"AssemblyName": "Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"ClassName": "Question49138328.TestClass",
"Signature": "Void <Test>b__0(Int32)",
"MemberType": 8,
"GenericArguments": null
}
}
The replacement FullTypeName is not included but everything else is. And as you can see, it's not actually storing the IL instructions of the delegate; it's storing the full signature of the method(s) to call, including the hidden, compiler-generated method name <Test>b__0 mentioned in this answer. You can see the hidden method name yourself just by printing a1.Method.Name.
Incidentally, to confirm that Json.NET is really saving the same member data as BinaryFormatter, you can serialize a1 to binary and print any embedded ASCII strings as follows:
var binary = BinaryFormatterHelper.ToBinary(a1);
var s = Regex.Replace(Encoding.ASCII.GetString(binary), #"[^\u0020-\u007E]", string.Empty);
Console.WriteLine(s);
Assert.IsTrue(s.Contains(a1.Method.Name)); // Always passes
Using the extension method:
public static partial class BinaryFormatterHelper
{
public static byte[] ToBinary<T>(T obj)
{
using (var stream = new MemoryStream())
{
new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter().Serialize(stream, obj);
return stream.ToArray();
}
}
}
Doing so results in the following string:
????"System.DelegateSerializationHolderDelegatemethod00System.DelegateSerializationHolder+DelegateEntry/System.Reflection.MemberInfoSerializationHolder0System.DelegateSerializationHolder+DelegateEntrytypeassemblytargettargetTypeAssemblytargetTypeNamemethodNamedelegateEntry0System.DelegateSerializationHolder+DelegateEntrylSystem.Action`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]Kmscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nullQuestion49138328.TestClass<Test>b__0/System.Reflection.MemberInfoSerializationHolderNameAssemblyNameClassNameSignatureMemberTypeGenericArgumentsSystem.Type[]Void <Test>b__0(Int32)
And the assert never fires, indicating that the compiler-generated method name <Test>b__0 is indeed present in the binary also.
Now, here's the scary part. If I modify my c# source code to create another Action<T> before a1, like so:
// I inserted this before a1 and then recompiled:
Action<int> a0 = (a) => Debug.WriteLine(a);
Action<int> a1 = (a) => Console.WriteLine(a);
Then re-build and re-run, a1.Method.Name changes to <Test>b__1:
{
"$type": "System.Action`1[[System.Int32, mscorlib]], mscorlib",
"Delegate": {
"$type": "System.DelegateSerializationHolder+DelegateEntry, mscorlib",
"type": "System.Action`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]",
"assembly": "mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"target": null,
"targetTypeAssembly": "Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"targetTypeName": "Question49138328.TestClass",
"methodName": "<Test>b__1",
"delegateEntry": null
},
"method0": {
"$type": "System.Reflection.RuntimeMethodInfo, mscorlib",
"Name": "<Test>b__1",
"AssemblyName": "Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"ClassName": "Question49138328.TestClass",
"Signature": "Void <Test>b__1(Int32)",
"MemberType": 8,
"GenericArguments": null
}
}
Now if I deserialize binary data for a1 saved from the earlier version, it comes back as a0! Thus, adding another delegate somewhere in your code base, or otherwise refactoring your code in an apparently harmless way, may cause previously serialized delegate data to be corrupt and fail or even possibly execute the wrong method when deserialized into the new version of your software. Further, this is unlikely to be fixable other than by reverting all changes out of your code and never making such changes again.
To sum up, we have found that serialized delegate information is incredibly fragile to seemingly-unrelated changes in one's code base. I would strongly recommend against persisting delegates through serialization with either BinaryFormatter or Json.NET. Instead, consider maintaining a table of named delegates and serializing the names, or following the command pattern and serialize command objects.

C# deserialization using reflection

I have a .dll that deserializes a class. When I call this .dll from a project or not using reflection it works fine. When I call the .dll using reflection I get an error on the line that deserializes. I know this is due isolation that happens when I use reflection to load an assembly. Wondering if anyone has any fix or an idea of how to implement this? BTW, the serialization works just fine, it's just the deserialization that doesnt work.
I tried both binary and xml, here's the code:
static public object SerializeLoad(string sFilename)
{
try
{
List<ElementTodo> _object = null;//ElementTodo _object = null;
Stream stream = File.Open(sFilename, FileMode.Open);
//BinaryFormatter bformatter = new BinaryFormatter();
XmlSerializer bformatter = new XmlSerializer(typeof(ElementTodo), "ToDo");
//_object = (_object.GetType())bformatter.Deserialize(stream);
_object = (List<ElementTodo>)bformatter.Deserialize(stream);
stream.Close();
return _object;
}
catch(Exception e)
{
string error = e.Message;
return null;
}
}
The generated XML is as follows:
<
?xml version="1.0"?>
<ArrayOfElementTodo xmlns:xsi="w3.org/2001/XMLSchema-instance"; xmlns:xsd="w3.org/2001/XMLSchema"; xmlns="ToDo">
<ElementTodo Title="a" content="aa" isDone="false" />
<ElementTodo Title="b" content="bb" isDone="false" />
<ElementTodo Title="c" content="cc" isDone="false" />
<ElementTodo Title="d" content="dd" isDone="false" />
</ArrayOfElementTodo>
I assume that ElementTodo is in an assembly that both your code and the assembly loaded using reflection have access to? What you have to be careful of is that your loaded assembly is using the same dependent assembly and doesn't load a new copy. Otherwise you wind up with fun errors like 'Object X (of type ElementTodo) is not of type ElementTodo', since two copies of the types are loaded. It's hard to say for sure that this is your issue without more information on the specific error, however.
If this is your problem, you can address it by forcing assemblies to resolve to the version that's already loaded using something like this:
In your startup code somewhere:
//This is required because we load assemblies at runtime
//If this is not used, there can be problems when Reflecting over Types
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
Implementation:
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
return AppDomain.CurrentDomain.GetAssemblies().
FirstOrDefault(assembly => assembly.FullName == args.Name);
}

XmlSerializer Deserialize failures

I have wsdl from third party server. Ran svcutil and ended up wih a set of
XmlNode AMethod(object Request);
methods. There is a separate 100 page pdf describing response/request objects for each method
My thought was wrap web methods and use XmlSerializer to return strongly typed objects. Returned xml looks like this (i removed soap headers):
<Response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="ResponseExt"
xmlns="http://www.thirdparty.com/lr/">
<Code>0</Code>
<Message>SUCCESS</Message>
<SessionId>session_token</SessionId>
</Response>
Looked simple. Created a class(from document/wire captures):
[XmlRoot("Response")]
//EDIT added XmlType
[XmlType("ResponseExt", Namespace = "http://www.thirdparty.com/lr/")]
public class MyClass {
public string Code {get; set;}
public string Message {get; set;}
public string SessionId {get; set;}
}
Processing time:
//XmlNode node = xml from above
XmlSerializer serializer = new XmlSerializer(typeof(MyClass));
XmlNodeReader reader = new XmlNodeReader(node);
Myclass myclass = serializer.Deserialize(reader) as MyClass
Last line is where it blows up with inner exception message: The specified type was not recognized: name='ResponseExt', namespace='http://www.thirdparty.com/lr/', at <Response xmlns=''>.
I can't figure out how to make Serializer happy and what exactly these two mean
xsi:type="ResponseExt"
xmlns="http://www.thirdparty.com/lr/
As always any advice and pointer are appreciated
EDIT: Accepted answer below.
I was still getting exception, until i found this, hopefully it'll save someone some time.
I started to work backwards. Captured xml on the wire. Deserialized to my created classes with correct attributes: worked like a charm. Tried again from webservice - exception. For some reason XmlSerializer doesn't recognize ResponseExt.
XmlSerializer serializer = new XmlSerializer(typeof(Response));
XmlNode node = (XmlNode)results[0];
XmlDocument doc = new XmlDocument();
doc.LoadXml(node.OuterXml); //reload node
XmlNodeReader reader = new XmlNodeReader(doc.FirstChild); //there is only one node
Response rsp = serializer.Deserialize(reader) as Response; //works
EDIT: underlying issue wsdl file was not complete. After spending 2 days on this and finding this (ugly) workaround, third-party vendor provided complete WSDL with all types that deserialize without errors.
Why are you manually deserializing XML, when you have WSDL ?
If you have WSDL, use the svcutil.exe tool, or the wsdl.exe tool, to generate proxy classes and DTOs for the XML messages being sent and received on the wire.
The point of a web services toolkit, or "stack" is to provide this for you, so that you don't have to author classes and XML serialization code by hand.
Did you try this? Did you try to run the WSDL through one of those tools? Or did you try to "Add web reference" in Visual Studio?
After updating the question, I suggest that you modify the WSDL, rather than write custom code. You can produce a custom WSDL for the service, which will correctly generate the proxy classes you want. If you don't need all 100 methods (or however many there are), then leave them out. If you want a custom object from a method, then define a complexType that corresponds to that object. This is much simpler and more reliable than hand-authoring XML deserialization code for each method.
If you don't like that idea, and want to stick with manually writin the XML deserialization code, then you need to do two things:
attach a namespace to the XmlRoot attribute.
change the name of your class to ResponseExt, and derive it from a class called Response. Decorate that Response class with an XmlInclude attribute. This aligns the use of the Xml Serializer with the xsi:type used in the XML fragment.
It looks like this in code:
[XmlRoot("Response", Namespace="http://www.thirdparty.com/lr/")]
public class ResponseExt : Response {
}
[XmlRoot("Response", Namespace="http://www.thirdparty.com/lr/")]
[XmlInclude(typeof(ResponseExt))]
public class Response {
public string Code {get; set;}
public string Message {get; set;}
public string SessionId {get; set;}
}
public class XsiType
{
public static void Main(string[] args)
{
try
{
string filename = "XsiType.xml";
XmlSerializer s1 = new XmlSerializer(typeof(Response));
ResponseExt r = null;
using(System.IO.StreamReader reader= System.IO.File.OpenText(filename))
{
r= (ResponseExt) s1.Deserialize(reader);
}
var builder = new System.Text.StringBuilder();
var xmlws = new System.Xml.XmlWriterSettings { OmitXmlDeclaration = true, Indent= true };
using ( var writer = System.Xml.XmlWriter.Create(builder, xmlws))
{
//s1.Serialize(writer, r, ns);
s1.Serialize(writer, r);
}
string xml = builder.ToString();
System.Console.WriteLine(xml);
}
catch (System.Exception exc1)
{
Console.WriteLine("Exception: {0}", exc1.ToString());
}
}
}
related: How can I force the use of an xsi:type attribute?

Serialization problem

We save and read files by (de) serializing a class named "DocumentClass".
All was working well, untill we added 2 more fields to the documentclass. (We think that's the problem)
When we now try to open files that were serialized by the previous edition, we get an error.
System.ArgumentException: Object of type 'System.Int32' cannot be converted to type 'System.String'.
at SoftwareProject.Componenten.Bestand.DocumentClass.d(String A_0)
at de..ctor(String A_0)
at g.a(String A_0)
The method generating the error is the method "Read". (DocumentClass.d() is the obfuscated name )
But things get weirder: when we open the file in VS debug mode, no error is generated, but all fields in the documentclass are 0 or null ???
We are lost here ... please help ...
We've added the [OptionalField] attribute to the new fields, but that doesn't help ..
Why are all values null in debug mode ??
And where is the runtime error coming from ? How can we debug it ?
Thanks in advance!!
public static DocumentClass Read(string fullFilePath)
{
DocumentClass c = new DocumentClass();
Stream s = File.OpenRead(fullFilePath);
BinaryFormatter b = new BinaryFormatter();
//b.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
b.Binder = new MyCustomBinder();
try
{
c = (DocumentClass)b.Deserialize(s);
}
catch( Exception exc )
{
s.Close();
throw exc;
}
finally
{
s.Close();
}
return c;
}
public class MyCustomBinder : SerializationBinder {
public override Type BindToType(string assemblyName, string typeName) {
Type tyType = null;
string sShortAssemblyName = assemblyName.Split(',')[0];
Assembly[] ayAssemblies = AppDomain.CurrentDomain.GetAssemblies();
if (sShortAssemblyName.ToLower() == "debugAssemblyName")
{
sShortAssemblyName = "AppAssemblyName";
}
foreach (Assembly ayAssembly in ayAssemblies) {
if (sShortAssemblyName == ayAssembly.FullName.Split(',')[0]) {
tyType = ayAssembly.GetType(typeName);
break;
}
}
return tyType;
}
}
.Net has something called "Version Tolerant Serialization" which most likely solves this issue ;)
You should check out this easy to understand example on object serialization:
http://programming.flashadventures.com/c-sharp/writing-objects-to-files-serialization/
I assume you are using BinaryFormatter? This serializer is notoriously brittle, since it (by default) includes the field-name in the stream; this impacts obfuscation particularly badly. Presumably the obfuscator is now choosing new named for the fields (perhaps at random, perhaps due to the new fields), and so it can't deserialize correctly.
A few options:
don't obfuscate the DTO
implement ISerializable so the field names don't matter
use a serializer that doesn't care about field names
I'd personally opt for the latter, but I'm a bit biased ;-p I know of people using protobuf-net with obfuscated classes; the data only includes numeric markers, so the meaning isn't really exposed (except of course, by inspection of the data - post codes etc, but that is the job of encryption).
I think you need to use Custom Serialization

Categories