I'm getting an exception with this code;
InnerException: System.InvalidOperationException
Message=The specified type was not recognized: name='Person',
namespace='', at "<"Contact xmlns=''>.
Here is the relevant code I think.
The class Person is a bare class without any anotations and doesn't inherit from any interface.
How to make Deserialize recognize my classes?
Thanks in advance.
public class Contacts : List<Contact.Contact>
{
private void PopulateTypeList()
{
types.Add(typeof(Contact.Contact));
types.Add(typeof(Contact.Company));
types.Add(typeof(Contact.Person));
types.Add(typeof(ContactData.Direction));
types.Add(typeof(ContactData.email));
types.Add(typeof(ContactData.Phone));
}
public void Load()
{
try
{
using (System.Xml.XmlReader stream = System.Xml.XmlReader.Create(fileName))
{
XmlSerializer xs = new XmlSerializer(typeof(List<Contact.Contact>));
// this roundabout way is for making it possible for this class to
// inherit from List<Contact.Contact> and still use a method that
// gives the stored data as an value in the object
here is error List<Contact.Contact> data =
(List<Contact.Contact>)xs.Deserialize(stream);
this.Clear();
this.AddRange(data);
}
}
catch (System.IO.FileNotFoundException)
{
// do nothing; no file, new database
}
}
public void Save()
{
using (System.Xml.XmlWriter stream = System.Xml.XmlWriter.Create(fileName))
{
XmlSerializer xs =
new XmlSerializer(typeof(List<Contact.Contact>), types.ToArray());
List<Contact.Contact> data = this.ToList();
xs.Serialize(stream, data);
}
}
Try passing the list of types to the serialiser you create for deserialisation:
XmlSerializer xs = new XmlSerialiser(typeof(List<Contact.Contact>), types.ToArray());
Related
I have a base class like :
public class Sensor
{
public void Serialize(string path)
{
try
{
System.Xml.Serialization.XmlSerializer xml = new System.Xml.Serialization.XmlSerializer(this.GetType());
using (System.IO.StreamWriter file = new System.IO.StreamWriter(System.IO.Path.GetFullPath(path)))
{
xml.Serialize(file, this);
}
}
catch (Exception e)
{
;
}
}
public static T Deserialize<T>(string path)
{
T loaded = default(T);
try
{
System.Xml.Serialization.XmlSerializer deserializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
using (StreamReader reader = new StreamReader(path))
{
loaded = (T)deserializer.Deserialize(reader);
}
}
catch (Exception e)
{
;
}
return loaded;
}
}
Then I have a couple of classes that derive from this:
public TemperatureSensor : Sensor {}
public PositionSensor :Sensor{}
They share some common interfaces but also implement things differently.
I have a SensorController that contains a List<Sensor> with a mixture of different sensors. I want to save them to XML files and load them afterwards.
I tried a simple:
public void Load()
{
var files = Directory.GetFiles(directory, "*.xml");
foreach(var file in files)
{
var p = CodePuzzle.Deserialize<Puzzle>(file);
}
}
The problem is that when the deserializer finds the <PositionSensor> it crashes (Unexpected <PositionSensor> at 2,2).. I guess it was expecting <Sensor>
How can that be done?? Loading each Sensor in the sub-class it was originally stored in??
First you should add the tag [XmlInclude(typeof(DerivedClass))] to the base Class. So it looks like:
[Serializable]
[XmlInclude(typeof(TemperatureSensor))]
[XmlInclude(typeof(PositionSensor))]
public Class Sensor
....
Then when you save to xml file any of the derived class, save them as Sensor, not as the derived class.
var myTemp = new TemperatureSensor();
Sensor.saveToXML(myTemp,"myTemp.xml")
Then when reading the xml´s in as Sensor, you can still identify the subclass they actually belong to.
Sensor myImportedSensor = Sensor.Load("myTemp.xml")
// myImportedSensor is Sebsir returns true
// AND
// myImportedSensor is TemperatureSensor returns also true
I need to make all my entities serializable. So I was thinking in a BaseEntity with a Backup and a Restore method. But in the restore I can't override the object with the saved one because this is read-only.
Any solution or some other way to get the serializable entities?
My code:
internal class BaseEntity
{
private MemoryStream ms = new MemoryStream();
private BinaryFormatter bf = new BinaryFormatter();
public void Backup()
{
bf.Serialize(ms, this);
}
public void Restore()
{
this = (BaseEntity)bf.Deserialize(ms);
}
}
The more common pattern is to not make it the responsibility of your objects to serialize/deserialize themselves; rather, use an external serializer:
var serializer = new DataContractJsonSerializer(typeof(YourClass));
var stream = ...;
YourClass yourObj = ...;
serializer.WriteObject(stream, yourObj);
var restoredObj = serializer.ReadObject(stream);
Edit: One way serialization can work is to use the System.Runtime.Serialization.Formatters.Binary.BinaryFormatter (or other implementation of IFormatter). To serialize an object you pass the object and a stream. To Deserialize the object, you pass a stream (positioned at the begining of your serialized data), and it returns the serialized object and all its depenedencies.
public static class EntityBackupServices
{
public static MemoryStream Backup (BaseEntity entity)
{
var ms = new MemoryStream();
Serialize (ms, entity);
ms.Position = 0;
return ms;
}
public static void Serialize (Stream stream, BaseEntity entity)
{
var binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize (stream, entity);
}
public static BaseEntity Restore (Stream stream)
{
var binaryFormatter = new BinaryFormatter();
var entity = (BaseEntity) binaryFormatter.Deserialize (stream);
return entity;
}
}
One thing a formatter don't do (though the FormatterServices class makes it possible) is modify existing objects. So you probably don't want to have an instance method called Deserialize. You can't really do this: new LionEntity().Deserialize () where it replaces the fields of an existing instance.
Note: You'll need to put Serializable over all your types. Any fields that can't be serialized (because it's either not a struct, or it's not marked as [Serializable] will need to be marked with NonSerialized.
// A test object that needs to be serialized.
[Serializable()]
public class BaseEntity
{
public int member1;
public string member2;
public string member3;
public double member4;
// A field that is not serialized.
[NonSerialized()] public MyRuntimeType memberThatIsNotSerializable;
public TestSimpleObject()
{
member1 = 11;
member2 = "hello";
member3 = "hello";
member4 = 3.14159265;
memberThatIsNotSerializable = new Form ();
}
public MemoryStream Backup ()
{
return EntityBackupServices.Backup (this);
}
}
Edit:
The way I've mentioned is a rather standard and accepted way. If you want to venture into hackdom, you can deserialize the object the way I've mentioned, then use reflection to set each field on your existing object to the value of the deserialized object.
public class BaseEntity
{
void Restore(Stream stream)
{
object deserialized = EntityBackupServices.RestoreDeserialize(stream);//As listed above
if (deserialized.GetType () != this.GetType ())
throw new Exception();
foreach (FieldInfo fi in GetType().GetFields())
{
fi.SetValue(this, fi.GetValue (deserialized));
}
}
}
public IEntidadBase Restore()
{
return (IEntidadBase)bf.Deserialize(ms);
}
#jacklondon how would you do EntitySerializer methods?
You can do serialization process with http://www.servicestack.net/ StackService.Text module for clean entities. You don't need any attribute (serializable/datacontract) in ms way.
public class EntityFoo
{
public string Bar { get; set; }
public EntityFoo (string bar)
{
Bar = bar;
}
}
public class EntityDumper //and the EntitySerializer
{
public static string Dump<T> (T entity)
{
return new TypeSerializer<T> ().SerializeToString (entity);
}
public static T LoadBack<T> (string dump)
{
return new TypeSerializer<T> ().DeserializeFromString (dump);
}
}
public class dump_usage
{
public void start ()
{
string dump = EntityDumper.Dump (new EntityFoo ("Space"));
EntityFoo loaded = EntityDumper.LoadBack<EntityFoo> (dump);
Debug.Assert (loaded.Bar == "Space");
}
}
I don't necessarily recommend this, but here is one pattern for an object that can persist and restore its own state using serialization that creates new instances:
public sealed class MyClass
{
private Data _data = new Data();
//Properties go here (access the public fields on _data)
public void Backup()
{
//Serialize Data
}
public void Restore()
{
//Deserialize Data and set new instance
}
private sealed class Data
{
//Public fields go here (they're private externally [because Data is private], but public to MyClass.)
}
}
Note that this only works if your serializer supports non-public classes. Worst-case, you have to make the nested class public, which is ugly, but doesn't hurt encapsulation (since the instance is private).
public void Save() {
XmlSerializer Serializer = new XmlSerializer(typeof(DatabaseInformation));
/*
A first chance exception of type 'System.IO.FileNotFoundException' occurred in mscorlib.dll
A first chance exception of type 'System.IO.FileNotFoundException' occurred in mscorlib.dll
A first chance exception of type 'System.InvalidOperationException' occurred in System.Xml.dll
*/
// ....
}
This is the whole class if you need it:
public class DatabaseInformation
{
/* Create new database */
public DatabaseInformation(string name) {
mName = name;
NeedsSaving = true;
mFieldsInfo = new List<DatabaseField>();
}
/* Read from file */
public static DatabaseInformation DeserializeFromFile(string xml_file_path)
{
XmlSerializer Serializer = new XmlSerializer(typeof(DatabaseInformation));
TextReader r = new StreamReader(xml_file_path);
DatabaseInformation ret = (DatabaseInformation)Serializer.Deserialize(r);
r.Close();
ret.NeedsSaving = false;
return ret;
}
/* Save */
public void Save() {
XmlSerializer Serializer = new XmlSerializer(typeof(DatabaseInformation));
if (!mNeedsSaving)
return;
TextWriter w = new StreamWriter(Path.Combine(Program.MainView.CommonDirectory.Get(), Name + ".xml"), false);
Serializer.Serialize(w, this);
w.Close();
NeedsSaving = false;
}
private string mName;
public string Name { get { return mName; } }
private bool mNeedsSaving;
public bool NeedsSaving { get { return mNeedsSaving; } set { mNeedsSaving = value; Program.MainView.UpdateTitle(value); } }
private bool mHasId;
public bool HasId { get { return mHasId; } }
List<DatabaseField> mFieldsInfo;
}
(PS: if you have any tips to improve my code feel free to share, I'm a C# beginner)
To serialize/deserialize your type it needs to have parameterless constructor. Check out here :
A class must have a default constructor to be serialized by
XmlSerializer.
oh.. I didn't know it had additional information (had to click "View detail.."), mystery solved:
Message=SDB.DatabaseInformation cannot
be serialized because it does not have
a parameterless constructor.
I was also getting this exception, but it wasn't due to missing a default constructor. I had some extra properties (a List and Dictionary) which aren't part of the XML document.
Decorating those properties with [XmlIgnore] solved the problem for me.
You can get around this by providing a default constructor that calls the overloaded constructor. For example:
public DatabaseInformation() : this ("defaultName"){}
I'm trying out Generics and I had this (not so) great idea of creating an XMLSerializer class. The code I pieced together is below:
public class Persist<T>
{
private string _path;
public Persist(string path) {
this._path = path;
}
public void save(T objectToSave)
{
XmlSerializer s = new XmlSerializer(typeof(T));
TextWriter w = new StreamWriter(this._path);
try { s.Serialize(w, objectToSave); }
catch (InvalidDataException e) { throw e; }
w.Close(); w.Dispose();
}
public T load()
{
XmlSerializer s = new XmlSerializer(typeof(T));
TextReader r = new StreamReader(this._path);
T obj;
try { obj = (T)s.Deserialize(r); }
catch (InvalidDataException e) { throw e; }
r.Close(); r.Dispose();
return obj;
}
}
Here's the problem: It works fine on Persist<List<string>> or Persist<List<int>> but not on Persist<List<userObject>> or any other custom (but serializable) objects. userObject itself is just a class with two {get;set;} properties, which I have serialized before.
I'm not sure if the problems on my Persist class (generics), XML Serialization code, or somewhere else :( Help is very much appreciated~
Edit:
code for userObject
public class userObject
{
public userObject(string id, string name)
{
this.id = id;
this.name = name;
}
public string id { get;private set; }
public string name { get;set; }
}
Looks to me like your code should just work - even though it does have a few flaws.
EDIT: Your userObject class isn't serializable. Xml serialization only works on types with a public, parameterless constructor - the current class won't work. Also, you should really rewrite your code to avoid explicit calls to .Close() or .Dispose() and instead prefer using where possible - as is, you might get random file locking if at any point during serialization an error occurs and your method terminates by exception - and thus doesn't call .Dispose().
Personally, I tend to use a just-for-serialization object hierarchy that's just a container for data stored in xml and avoids any behavior - particularly side effects. Then you can use a handly little base class that makes this simple.
What I use in my projects is the following:
public class XmlSerializableBase<T> where T : XmlSerializableBase<T>
{
static XmlSerializer serializer = new XmlSerializer(typeof(T));
public static T Deserialize(XmlReader from) { return (T)serializer.Deserialize(from); }
public void SerializeTo(Stream s) { serializer.Serialize(s, this); }
public void SerializeTo(TextWriter w) { serializer.Serialize(w, this); }
public void SerializeTo(XmlWriter xw) { serializer.Serialize(xw, this); }
}
...which caches the serializer in a static object, and simplifies usage (no generic type-paramenters needed at call-locations.
Real-life classes using it:
public class ArtistTopTracks {
public string name;
public string mbid;//always empty
public long reach;
public string url;
}
[XmlRoot("mostknowntracks")]
public class ApiArtistTopTracks : XmlSerializableBase<ApiArtistTopTracks> {
[XmlAttribute]
public string artist;
[XmlElement("track")]
public ArtistTopTracks[] track;
}
Sample serialization calls:
using (var xmlReader = XmlReader.Create([...]))
return ApiArtistTopTracks.Deserialize(xmlReader);
//[...]
ApiArtistTopTracks toptracks = [...];
toptracks.SerializeTo(Console.Out);
There can be a number of reasons why your code fails: This text is particularly helpful when having issues: Troubleshooting Common Problems with the XmlSerializer . Maybe you have some type hierarchy in your user objects and the serializer does not know about it?
I want to fill items in a combobox, each of them has different behaviour.
Yes I know I could simply create 3 classes deriving from a base class. But my question is kind of "is there another way" and "what is possible".
In Java one can do "new MyClass(){public void overriddenmethod(){...} }" but in C# we can not, can we?
Now I use a lambda to define a method on the fly but the problem is that I later want the new XxxFormatter() as instance variable of that object. Since the XxxFormatters share no common base class I cannot put them as a single field in the SerializingHelper class.
Do you have any Ideas?
public delegate void SerializingHandler(Stream s, object o);
class SerializingHelper
{
public string Name { get; set; }
public SerializingHandler Serializer { get; set; }
}
comboFormat.Items.AddRange(new object[]
{
new SerializingHelper{ Name = "Binary",
Serializer = (s,o)=>new BinaryFormatter().Serialize(s,o),
new SerializingHelper{ Name = "Soap",
Serializer = (s,o)=>new SoapFormatter().Serialize(s,o),
new SerializingHelper{ Name = "Xml",
Serializer = (s,o)=>new XmlSerializer(typeof(KontaktpartnerData), new Type[]
{typeof(ArrayList), typeof(KontaktPartner)}).Serialize(s,o), }
});
If you just want to prevent instantiating a new serializer instance each time, you can instantiate them outside of the lambda:
var binaryFormatter = new BinaryFormatter();
comboFormat.Items.AddRange(new object[]
{
new SerializingHelper
{
Name = "Binary",
Serializer = binaryFormatter.Serialize
}
...
});
If you really need to store the formatter as a field, you could do something like this:
delegate void SerializingHandler<TFormatter>(TFormatter formatter,
Stream stream,
object graph);
interface ISerializingHelper
{
void Serialize(Stream stream, object graph);
}
class SerializingHelper<TFormatter> : ISerializingHelper
{
private readonly SerializingHandler<TFormatter> handler;
private readonly TFormatter formatter;
public SerializingHelper(SerializingHandler<TFormatter> handler,
TFormatter formatter)
{
this.handler = handler;
this.formatter = formatter;
}
public TFormatter Formatter
{
get { return this.formatter; }
}
public void Serialize(Stream stream, object graph)
{
this.handler(this.formatter, stream, graph);
}
}