I created a custom control in XAML, and added some custom properties as well. Now, I want to serialize it to JSON if possible. Here is (essentially) what I have:
public partial class MyCustomClass : UserControl
{
public Dictionary<char, int[]> ValueMap;
public int Value { get; set; }
}
And in the code that handles serialization:
public static string Serialize(object objectToSerialize)
{
using (MemoryStream ms = new MemoryStream())
{
DataContractJsonSerializer serializer =
new DataContractJsonSerializer(objectToSerialize.GetType());
serializer.WriteObject(ms, objectToSerialize);
ms.Position = 0;
using (StreamReader reader = new StreamReader(ms))
return reader.ReadToEnd();
}
}
However, serializer.WriteObject(ms, objectToSerialize); throws
System.Runtime.Serialization.InvalidDataContractException:
Consider marking it with the
DataContractAttribute attribute, and
marking all of its members you want
serialized with the
DataMemberAttribute attribute.
Alternatively, you can ensure that the
type is public and has a parameterless
constructor - all public members of
the type will then be serialized, and
no attributes will be required."
Now, when I do add those attributes to the MyCustomClass, I of course get the same exception, only this time for System.Windows.UIElement instead of MyCustomClass.
So, is there a way to serialize my custom derived class with the existing serialization method, or should I just write a custom serialization methods for MyCustomClass?
I think you are better off implementing IXmlSerializable here, as you really don't want to indiscriminately serialize everything in the base class (and I don't believe you can, quite frankly).
Rather, implement IXmlSerializable on MyCustomClass, and then the DataContractJsonSerializer will be able to use that implementation to serialize to/from JSON.
Related
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);
I get the following error when trying to serialize a generic list
"System.Reflection.RuntimeParameterInfo is inaccessible due to its protection level. Only public types can be processed"
here is my code below
XmlSerializer serializer = new XmlSerializer(typeof(List<System.Reflection.ParameterInfo>));
XDocument document = new XDocument();
using (XmlWriter xmlWriter = document.CreateWriter())
{
serializer.Serialize(xmlWriter, parameterList);
}
return document.Root.ToString();
Not all types are serializable. With a serializer such as XmlSerializer, your best approach is to write a DTO that has the parts that you are interested in, for example:
public class Parameter {
public string Name {get;set;}
public string TypeName {get;set;}
public bool ByRef {get;set;}
}
then populate that from the ParameterInfo, and serialize that. Note that some things are problematic here, for example it would be very inconvenient to serialize a DefaultValue, unless you serialize it as a string and parse it back at the receiver.
I have a class which inherits from a collection, specifically List<> and I've listed the class below. The problem I'm encountering when I serialize the object to XML using DataContractSerializer is the additional fields I've added within the object are not getting serialized by the serializer.
Here is the class:
[CollectionDataContract(Name = "ServiceEvents", Namespace = "")]
public class ServiceEventList : List<ServiceEvent>
{
[DataMember]
public long StaleDate { get; set; }
[DataMember]
public long ExpirationDate { get; set; }
}
When I serialize the object and write to disk, here is the output (notice both StaleDate and ExpirationDate are missing).
<ServiceEvents xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><ServiceEvent><Date>2012-06-26T22:23:24.120817Z</Date><Description>A Service Event</Description><Id>634763462041210040</Id><Notes></Notes><Odometer>42</Odometer></ServiceEvent></ServiceEvents>
Here is the code that serializes the object:
using (FileStream fs = new FileStream(path, FileMode.Create))
{
//TODO: StaleDate is not serializing to disk
//TODO: ExpirationDate is not serializing to disk
DataContractSerializer ser = new DataContractSerializer(typeof(ServiceEventList));
ser.WriteObject(fs, list);
}
My next thought is to remove the inheritance structure and just embed a List object into the class. I'd prefer to just extend List but won't waste more time on it if the community confirms my approach won't work. Thanks in advance for the advice.
According to this post:
http://social.msdn.microsoft.com/Forums/eu/wcf/thread/57eb195a-43a9-47fe-8b1a-a9d23feb2df2
the problem is indeed that you inherit from a collection type - in this case the DataContractSerializer serializes only its items, but not any extra properties.
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.
So I need to serialize a generic dictionary like Dictionary<long, List<MyClass>>. I need to serialize it to store it in the ViewState of an ASP.Net application.
I found an example of using TypeConverter to convert my class to a string to be serialized, but I get an error message saying MyClass is not marked as serializable.
Here is the code for my class..
[TypeConverter (typeof(MyClass_Converter))]
public class MyClass
{
// some properties
}
public class MyClass_Converter : System.ComponentModel.TypeConverter
{
public override bool CanConvertTo(...)
{
// code
}
// CanConvertFrom, ConvertFrom, ConvertTo methods
}
Then when I want to serialize it, I am using this code...
LosFormatter los = new LosFormatter();
StringWriter sw = new StringWriter();
los.Serialize(sw, hiddenData);
String resultSt = sw.GetStringBuilder().ToString();
ViewState["HiddenData"] = resultSt;
Am I doing something wrong?
Add the [Serializable] attribute to your class.
http://msdn.microsoft.com/en-us/library/system.serializableattribute.aspx