I have a class that stores a ObservableCollection of BitmapImage's and I need to store this information in IsolatedStorage. I am using DataContract to do this with other class's which works. I am thinking that not being able to set the BitmapImage as a DataMember is stopping it from working.
Error message is:
Type 'System.Windows.Media.ImageSource' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute.
Any help would be appreciated.
CODE - Save to IsolatedStorage
public static void AddOrUpdateUnsavedItems(string key, List<ProcessClass> unsavedItems)
{
var store = IsolatedStorageFile.GetUserStoreForApplication();
if (store.FileExists("unsaveditem"))
{
store.DeleteFile("unsaveditem");
}
using (var stream = new IsolatedStorageFileStream("unsaveditem", FileMode.OpenOrCreate, FileAccess.Write, store))
{
DataContractSerializer dcs = new DataContractSerializer(typeof(List<ProcessClass>));
dcs.WriteObject(stream, unsavedItems);
}
}
CODE - Class
[DataContract]
public class ProcessClass
{
ObservableCollection<BitmapImage> images = new ObservableCollection<BitmapImage>();
[DataMember]
public ObservableCollection<BitmapImage> Images
{
get { return images; }
set { images = value; }
}
[DataMember]
public string Notes { get; set; }
[DataMember]
public string ItemID { get; set; }
}
BitmapImage is simply not serializable using data contract and there is no way to change this by yourself.
The only way to persist a BitmapImage (it's an image) is to use BinaryWriter or WriteableBitmap. An image converter for tranforming BitmapImage to byte[] and vice-versa will help too.
Related
So I have a class called OutputInformation which I'd like to store and then read on another computer to retrieve the data.
I'm using binary serialization.
[Serializable()]
public class OutputInformation
{
public List<string> filenames { get; set; }
public long[] filesizes { get; set; }
}
public void Write()
{
OutputInformation V = new OutputInformation();
V.filesizes = sizearray;
V.filenames = namelist;
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream("D:\\MyFile.bin", FileMode.Create,
FileAccess.Write, FileShare.None);
formatter.Serialize(stream, V);
stream.Close();
}
The serialization is done within a user control and if I deserialize in the user control, it works fine.
But if I try to deserialize from my main window, I get an invalidcastexception. So I would imagine the same problem would occur if I tried to deserialize the file from another computer.
How do I solve this? I just need to store the class in a file and retrieve it later from a different computer. Preferably not use XML serialization.
[Serializable()]
public class OutputInformation
{
public List<string> filenames { get; set; }
public long[] filesizes { get; set; }
}
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream("D:\\MyFile.bin", FileMode.Open,
FileAccess.Read, FileShare.Read);
OutputInformation obj = (OutputInformation)formatter.Deserialize(stream);
stream.Close();
Error is an InvalidCastException. Additional information: [A]OutputInformation cannot be cast to [B]OutputInformation.
Both classes should be in the same namespace. Define your OutputInformation class on deserialization in exact the same namespace as on serialization (or refer assembly with it).
Or, if it is not possible, or you prefer not to do it, you should write your own implementation of SerializationBinder
public class ClassOneToNumberOneBinder : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
typeName = typeName.Replace(
"oldNamespace.OutputInformation",
"newNamespace.OutputInformation");
return Type.GetType(typeName);
}
}
And set it before deserialization:
formatter.Binder = new ClassOneToNumberOneBinder();
Look here for more details: Is it possible to recover an object serialized via "BinaryFormatter" after changing class names?
The following codes works as is, but I would like to use a reference to the MyProperty class to be passed in the constructor
instead of the strongly typed references in the inline code.
How do I do this, I expected to pass a ref to MyProperty but everything I have tried fails
I would like PropertyClass to be able to handle any MyProperty classes i.e. no references to MyProperty in PropertyClass
Still learning so sorry if I have missed the obvious !
Many Thanks for any help
Sarah
PropertyClass pc = new PropertyClass(!here!); // Would like to pass MyProperty class here
pc.Settings.Add(new MyProperty("Fred", "Monday"));
pc.SaveXml("MyTest.xml");
public class MyProperty
{
[XmlAttribute]
public string MyPropName { get; set; }
[XmlElement]
public string MyPropData { get; set; }
// default constructor needs to be parameterless for serialisation.
public MyProperty()
{
}
public MyProperty(string Name, string Data)
{
MyPropName = Name;
MyPropData = Data;
}
}
public class PropertyClass
{
public List<MyProperty> Settings { get; set; }
public PropertyClass() // How to pass the required class here ?
{ // public PropertyClass( ref MyProperty myprop)
Settings = new List<MyProperty>();
}
public void SaveXml(string fileName)
{
using (FileStream stream = new FileStream(fileName, FileMode.Create))
{
XmlSerializer XML = new XmlSerializer(typeof(List<MyProperty>), new XmlRootAttribute("Settings"));
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add(string.Empty, string.Empty);
XML.Serialize(stream, Settings, namespaces);
}
}
}
I would change the definition of PropertyClass to
public class PropertyClass<T>
{
public List<T> Settings { get; set; }
public PropertyClass()
{
Settings = new List<T>();
}
public void SaveXml(string fileName)
{
using (FileStream stream = new FileStream(fileName, FileMode.Create))
{
XmlSerializer XML = new XmlSerializer(typeof(List<T>), new XmlRootAttribute("Settings"));
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add(string.Empty, string.Empty);
XML.Serialize(stream, Settings, namespaces);
}
}
}
The type parameter T specifies the type of the items in the List<T>, so that you can instantiate PropertyClass as follows
var pc = new PropertyClass<MyProperty>();
Or when you get tired of MyProperty you can change it to new PropertyClass<foo>() without changing it elsewhere.
Another nice feature that I like about generics is that you can actually place constraints on the type parameter in the line where you declare it like:
public class PropertyClass<T> where T : MyClass, IMyInterface, new()
This means that T has to be derived from MyClass, it has to implement IMyInterface and has to have a parameterless constructor. (Obviously you do not need to add all such constraints, but they can all be useful in certain cases).
I want to rant a little more, but I am sure you can play with it and find some uses for it.
I think you need a generic class.
public class PropertyClass<T>
{
public List<T> Settings { get; set; }
public PropertyClass()
{
Settings = new List<T>();
}
...
}
PropertyClass<MyProperty> pc = new PropertyClass<MyProperty>();
I must add that your naming is very unclear. PropertyClass should be called something like XmlableList. And MyProperty already exists and is called NameValuePair<string,string>
Possibly you are looking for generics:
public class PropertyClass<TMyProperty>
{
public List<TMyProperty> Settings { get; set; }
public PropertyClass()
{
Settings = new List<TMyProperty>();
}
..
}
Work on your naming, it wasn't immediately obvious that PropertyClass was actually a collection of properties; perhaps MyPropertyCollection would be better?
What you're looking for is called constructor overloading. Basically you specify the constructor again, but this time with parameters:
public MyPropertyCollection()
{
Settings = new List<MyProperty>();
}
public MyPropertyCollection(IEnumerable<MyProperty> collection)
{
Settings = new List<MyProperty>(collection);
}
Or to allow var col = new MyPropertyCollection(new MyProperty(), new MyProperty(), new MyProperty()) you can do:
public MyPropertyCollection(params MyProperty[] collection)
{
Settings = new List<MyProperty>(collection);
}
Though you should be careful with that, it doesn't feel right and should you later want to introduce additional parameters, well it could end bad.
Also, as you're basically wrapping a list, what you could also consider is the System.Collection.ObjectModel.Collection<T> class as a base:
// The Collection<MyProperty> base class is responsible for maintaining the list
public class MyPropertyCollection : Collection<MyProperty>
{
public MyPropertyCollection()
{
// Default base() constructor is called automatically
}
public MyPropertyCollection(IList<MyProperty> properties)
: base(properties)
{
// Overloaded constructor calls base constructor with collection of properties
}
public void SaveXml(string fileName)
{
using (var stream = new FileStream(fileName, FileMode.Create))
{
// Serializer should now target this very class
var xml = new XmlSerializer(typeof (this), new XmlRootAttribute("Settings"));
var namespaces = new XmlSerializerNamespaces();
namespaces.Add(string.Empty, string.Empty);
xml.Serialize(stream, this, namespaces);
}
}
}
You need to add a constructor that takes a MyProperty as an argument:
public PropertyClass(MyProperty myprop)
{
Settings = new List<MyProperty> {myprop};
}
Note that MyProperty is a reference type so ref is unnecessary here (it already is a reference).
this is how you call the constructor
PropertyClass pc = new PropertyClass(new MyProperty("Fred", "Monday"));
this is the constructor
Public MyProperty MyProperty { get; set; }
public PropertyClass(MyProperty _myProperty)
{
MyProperty = _myProperty
}
Say I have the following classes:
[DataContract]
class Entry<T>
{
[DataMember]
public T Data { get; set; }
}
[DataContract]
class DataList<T>
{
[DataMember]
public IList<T> Collection { get; set; }
}
[DataContract]
[KnownType(typeof(User))]
class ExtendedDataList : DataList<object>
{
[DataMember]
public string SomeExtraParameter { get; set; }
}
[DataContract]
class User
{
[DataMember]
public string Name { get; set; }
}
I've applied KnownTypeAttribute to ExtendedDataList since that class extends the base class of general type object, and it will store different kinds of objects in the list. In this example, I've marked a known type of User since I know it'll contain User objects.
Here's the serialization code:
var user = new User { Name = "Bob" };
var users = new ExtendedDataList { Collection = new List<object> { user } };
serialize(users);
with
static void serialize<T>(T obj)
{
var entry = new Entry<T>();
entry.Data = obj;
var stream = new MemoryStream();
var serializer = new DataContractJsonSerializer(typeof(Entry<T>));
serializer.WriteObject(stream, entry);
stream.Seek(0, SeekOrigin.Begin);
var r = new StreamReader(stream);
var s = r.ReadToEnd();
System.Diagnostics.Debug.WriteLine(s);
}
the line serializer.WriteObject(stream, entry); throws a SerializationException saying that the type User was not expected and that I should use KnownTypeAttribute to specify it. But I did (indirectly)!
How can I make this work? I cannot move KnownType to the Entry<T> class, because it needs to be general. Why can't DataContractJsonSerializer see that ExtendedDataList specifies the User as a known type?
I figured out a way to get it to work, but it still doesn't explain why DataContractJsonSerializer is ignoring the KnownType attribute I've applied to ExtendedDataList.
It seems that DataContractJsonSerializer isn't smart enough to notice the KnownType attributes that I've specified on the ExtendedDataList class; apparently it only honors the known types that I've attached to the Entry<T> class since typeof(Entry<T>) is the type that I give to the constructor of DataContractJsonSerializer
I want Entry<T> to remain general, and I do not want to litter it with all sorts of KnownType attributes. So instead, I use the following class definition:
[DataContract]
[KnownType("KnownTypes")]
class Entry<T>
{
[DataMember]
public T Data { get; set; }
public static IEnumerable<Type> KnownTypes()
{
var attrs = typeof(T).GetTypeInfo().GetCustomAttributes<KnownTypeAttribute>();
return attrs.Select(attr => attr.Type);
}
}
This uses reflection to get the KnownType attributes attached to the inner type T and then passes these types to whatever data contract serializer that serializes Entry<T>.
Note: requires using System.Linq and using System.Reflection.
In my class (Camera), that I need to serialize, I have one member which doesn't have much serialization means and I can't modify its class. But there are only 2-3 parameters out of whole class that I can serialize and then de-serialize based on that.
XmlSerializer SerializerObj = new XmlSerializer(typeof(Camera));
TextWriter WriteFileStream = new StreamWriter(#"C:\test.xml");
SerializerObj.Serialize(WriteFileStream, cc);
WriteFileStream.Close();
How and where can I put custom serialization code, to take those 2-3 parameters from object and give to serializer, then during de-serialization create again that object using those 2-3 parameters.
Update. The example code:
[Serializable()]
public class Camera
{
public string name;
public int index;
public double distance;
public List<string> CameraList { get; set; }
[XmlIgnore()]
public GMarkerGoogle marker;
public Camera()
{
}
}
marker is the member that doesn't have serialization means. But there with marker.position, marker.rotation I can re-create that object.
If you can't or don't want to Xml serialize your GMarker then make sure that you do serialize the properties needed to reconstruct it (kind of what you already suggested). That way you can make a property with backing field to lazy recreate the GMarker.
[Serializable()]
public class Camera
{
public string name;
public int index;
public double distance;
public List<string> CameraList { get; set; }
private GMarkerGoogle _marker;
[XmlIgnore()]
public GMarkerGoogle Marker
{
set
{
_marker = value;
MarkerPosition = _marker.position;
MarkerRotation = _marker.rotation;
}
get
{
if (_marker == null)
{
_marker = new GMarkerGoogle(MarkerPosition, MarkerRotation);
}
return _marker;
}
}
public double MarkerPosition { get; set; }
public double MarkerRotation { get; set; }
public Camera()
{
}
}
Does this help? I made up a thing or two because I don't know the API for the marker, but I hope you can draw the picture from this point onwards.
By implementing the IXmlSerializable interface in your Camera class.
Check MSDN for more info.
Alternatively you could use the [XmlIgnore] attribute to flag the properties you do not want to serialize to XML.
Check MSDN for more info :).
PS: If you post some code I can show you an example but you probably don't need me. Let me know if you do though.
I have two classes SccmAction and TicketAction which both implement interface IDelivery. These classes will be transmitted on a processing queue from which I just want to pull the message and act upon the Deliver method.
It seems however that I cannot deserialize to an interface because when I attempt to do so a System.NotSupportedException is thrown. From what I have gathered the XMLSerializer requires a concrete type for serialization. Does this leave me having to create an abstract class which implements IDelivery and inheriting SccmAction and ArsAction from it? Have I missed something that?
Edit Further Clarification
I may have a design flaw but my purpose is to pull messages off of a queue containing these objects. My intention was to have all objects coming off of this queue implement the interface ensuring that the application processing the objects just operates on the Deliver method. Each objects implementation of Deliver would vary.
Code to Deserialize
using (SqlConnection connection =
new SqlConnection(ConnectionString))
{
SqlCommand command = new SqlCommand(ReceiveString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
byte[] sb = (byte[])reader["message_body"];
SccmAction sa = DeserializeObject<SccmAction>(sb);
IDelivery iD = DeserializeObject<IDelivery>(sb);
}
reader.Close();
}
public static T DeserializeObject<T>(byte[] ba)
{
XmlSerializer xs = new XmlSerializer(typeof(T));
MemoryStream memoryStream = new MemoryStream(ba);
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
return (T)xs.Deserialize(memoryStream);
}
Classes and Interface
[Serializable]
public class SccmAction : IDelivery
{
public string MachineName { get; set; }
public string CollectionName { get; set; }
public string Action { get; set; }
public DateTime EntryDateTime { get; set; }
public SccmAction () { }
public void Deliver()
{
throw new NotImplementedException();
}
}
[Serializable]
public class ArsAction : IDelivery
{
public string MachineName { get; set; }
public string CustomerName { get; set; }
public ArsAction() { }
public void Deliver()
{
throw new NotImplementedException();
}
}
public interface IDelivery
{
void Deliver();
}
Simply put, you can't serialize an interface without any dirtiness. But why do you want to serialize your interface anyway, it doesn't hold any state. It only has a declaration for a method.
You might want to create a Serialize method inside your classes. So they can serialize themselves individually, they know which type they are.
Besides, how do you expect the XmlSerializer to Deserialize to an exact type without providing it? It can't simply pick what to Deserialize to....
You could however change this line
IDelivery iD = DeserializeObject<IDelivery>(sb);
to this
IDelivery iD = DeserializeObject<SccmAction>(sb);
If you dont know what type you are deserializing to initially (ArsAction or SccmAction), then you can do something like this.
public static IDelivery DeserializeFromString(string xml)
{
//replace this stream with whatever you are useing
StringReader strReader = new StringReader(xml);
XmlReader reader = new XmlTextReader(fs); //important to use XmlReader
reader.MoveToContent(); //move to root
String className = reader.Name.Trim(); //read the class name
//use the namespace IDelivery is located in
className = "IDeliveryNamespace." + className;
//get the type
Type classType = Type.GetType(className);
XmlSerializer serializer = new XmlSerializer(Type.GetType(className));
// Declare an object variable of the type to be deserialized.
IDelivery i;
// Use the Deserialize method to restore the object's state.
i = (IDelivery)Convert.ChangeType(serializer.Deserialize(reader),classType);
return i;
}
Of course this assumes that you your Serializable class Name is not changed. Meaning ArsAction class serializes to and as far as I can tell from what you posted that is the case.
This code is a little dirty, but it should give you a starting point.