I have a Class A and Class B that contains List of class A objects.
public class Item
{
public string X{ get; set; }
public string Y{ get; set; }
}
public class ItemCollection
{
List<Item> items = new List<Item>();
//Some methods
}
I can serialize it by:
IsolatedStorageFileStream ifs = new IsolatedStorageFileStream("myxml.xml", FileMode.Create, isfile);
DataContractSerializer ser = new DataContractSerializer(itemlist.items.GetType());
XmlWriter writer = XmlWriter.Create(ifs);
ser.WriteObject(writer, itemlist.items);
But while deserializing it, I get "... Root element is missing." error.
IsolatedStorageFileStream ifs = new IsolatedStorageFileStream("myxml.xml", FileMode.Open, isfile);
DataContractSerializer ser = new DataContractSerializer(itemlist.Items.GetType());
XmlReader reader=XmlReader.Create(ifs);
itemlist.items= (List<Item>)ser.ReadObject(reader);
Is there any other/better way of serializing/deserializing one class that contains list/collection of another class?
It looks to me like the problem is simply that you are wiping the file when you open it to deserialize, via FileMode.Create. So of course the root element is missing: it is empty. You probably have the FileMode.Create and FileMode.Open inverted between the serialize/deserialize. And then add a few using for good measure.
The serialize should use FileMode.Create, to truncate or create the file fresh for new data.
The deserialize should use FileMode.Open, to look at the existing data.
Related
For a project that I'm working on I need to save the state of all variables in a console application.
I know that I can write individual variables to a file, but I would like to know if there is any way to do this for every variable with a value in the program.
Is this possible, and if so, how would I accomplish it?
Depending on the scenario. I would create a serializable class that holds all the data to be serialized and then serialize the class to a file before exiting the application.
Then, deserialize the file (if exists) when the application starts. The class needs to be marked as Serializable.
[Serializable]
public class AppState
{
public int Var1 { get; set; }
public long Var2 { get; set; }
public String Var3 { get; set; }
}
Then, to serialize an instance of that class as binary...
var state = new AppState();
state.Var1 = 4;
//TODO: populate the state object
var formatter = new BinaryFormatter();
using(var stream = new FileStream("C:\\app.state", FileMode.Create, FileAccess.Write))
{
formatter.Serialize(stream, state);
stream.Close();
}
To reload state, first make sure the file exists...
if(File.Exists("C:\\path_to_file")){
//TODO: do whatever you need to here, such as deserializing the file
}
Then deserialize and initialize all required variables...
var formatter = new BinaryFormatter();
AppState state = null;
using(var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
state = (AppState) formatter.Deserialize(stream);
}
if(state != null){
//TODO: initialize state
}
Note that there are many ways to serialize data. The most popular one are:
JSON serialization
XML
Binary (as explained above)
Use your projects Settings for each variable and then, use Properties.Settings.Default.Save() whenever you need to.
Right click your project from solution explorer > Settings.
Create a setting file if needed, then create your variables.
To use then: Properties.Settings.Default.VariableName1;
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?
TL, DR: we've been serializing some data in some tables of our SQL DB. Unfortunately that serialization technique wastes a lot of space for markup characters. We've found a new, more efficient way: to keep backwards compatibility, is it safe to adopt the following logic? -> When the serialization occurs, it always occurs using the new, more efficient way. When deserialization occurs, we check whether the string uses the new serialization format or the old one --> we then deserialize with the appropriate method. Is this robust? Can this be used in production? Aren't there any subtle problems with this approach?
Greetings. I'm working on an application which interacts with a SQL database. To achieve a specific business requirement, we've been serializing some data in a special column of our DB tables, of type ntext. Basically, in each cell of this column, we serialize an array of "Attributo" object, so typeof(T) is Attributo[]:
The "Attributo" definition is like the following:
public class Attributo
{
public virtual String Nome { get; set; }
public virtual String Valore { get; set; }
public virtual String Tipologia { get; set; }
}
- Deserialization to read the actual values:
XMLUtilities.Deserialize<Attributo[]>(value));
Serialization to store the values in the column (for each row..):
XMLUtilities.Serialize(attributes.ToArray());
And this is the helper class, which makes use of the XmlSerializer object:
public static class XMLUtilities
{
public static T Deserialize<T>(String xmlString)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (TextReader reader = new StringReader(xmlString))
{
return (T) serializer.Deserialize(reader);
}
}
public static String Serialize<T>(T xmlObject)
{
MemoryStream stream = new MemoryStream();
XmlSerializer serializer = new XmlSerializer(typeof(T));
XmlSerializerNamespaces xmlnsEmpty = new XmlSerializerNamespaces();
xmlnsEmpty.Add("", "");
serializer.Serialize(stream, xmlObject, xmlnsEmpty);
stream.Seek(0, SeekOrigin.Begin);
using (StreamReader reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
}
Now, the problem with this technique is that it wastes a lot of space for markup characters. This is an example string which is stored on the db:
<?xml version="1.0"?>
<ArrayOfAttributo>
<Attributo>
<Nome>Leakage_test_Time_prg1_p1</Nome>
<Valore>4</Valore>
<Tipologia>Single</Tipologia>
</Attributo>
<Attributo>
<Nome>Leakage_air_Volume_p1</Nome>
<Valore>14</Valore>
<Tipologia>Single</Tipologia>
</Attributo>
</ArrayOfAttributo>
So, we've found a more concise way of serializing these Attributo[], which produces this kind of output:
<ArrayOfA xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<A>
<N>Leakage_test_Time_prg1_p1</N>
<V>4</V>
<T>Single</T>
</A>
<A>
<N>Leakage_air_Volume_p1</N>
<V>14</V>
<T>Single</T>
</A>
</ArrayOfA>
then, to preserve backwards compatibility, which is the core issue we have implemented the following logic:
During serialization:
we always serialize in the new, more concise fashion
During deserialization:
we check whether the string starts with:
<?xml version="1.0"?>
or not. If that is the case, this is an old entry so we deserialize it in the old way. Otherwise, we deserialize using the new format.
We achieved that by decorating "Attributo" this way:
[DataContract(Name = "A", Namespace= "")]
public class Attributo
{
[DataMember(Name = "N")]
public virtual String Nome { get; set; }
[DataMember(Name = "V")]
public virtual String Valore { get; set; }
[DataMember(Name = "T")]
public virtual String Tipologia { get; set; }
}
and by performing the following changes to our serialization/deserialization methods, which now, for the new serialization technique, rely on DataContractSerializer object:
public static T Deserialize<T>(String xmlString)
{
//let's see if it's an old-style entry...
if (xmlString.StartsWith("<?xml version=\"1.0\"?>\r\n<ArrayOfAttributo>"))
{
try
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (TextReader reader = new StringReader(xmlString))
{
return (T)serializer.Deserialize(reader);
}
}
catch { }
}
//..then it must be a new-style one
DataContractSerializer ser = new DataContractSerializer(typeof(T));
using (Stream s = _streamFromString(xmlString))
{
return (T) ser.ReadObject(s);
}
}
public static String Serialize<T>(T xmlObject)
{
MemoryStream stream1 = new MemoryStream();
DataContractSerializer ser = new DataContractSerializer(typeof(T));
ser.WriteObject(stream1, xmlObject);
stream1.Position = 0;
StreamReader sr = new StreamReader(stream1);
string xmlString = sr.ReadToEnd();
return xmlString;
}
private static Stream _streamFromString(string s)
{
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(s);
writer.Flush();
stream.Position = 0;
return stream;
}
Everything seems to be working with this approach but we want to assess every possible risk before proceeding any further. Is this safe to use in production?
One more thing to keep in mind, while deserializing the older entry :
deserialize old-entry in old-style
serialize the old-entry in new-style
save the new-style-serialized-old-entry, and delete the old-style-serialized-old-entry.
You're good to go.
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'm using XMLSerializer to load some objects:
using (FileStream fileStream = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Read, FileShare.None))
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof (ModelDescriptor));
modelDescriptor = (ModelDescriptor) xmlSerializer.Deserialize(fileStream);
}
This will load a ModelDescriptor object with the data from an XML file. However, how do I load multiple objects this way? I think I would need a loop, but is there any way to know ahead of time how many objects there are? I get an InvalidOperationException if I overshoot the list by trying to load an object from the XML file that isn't there. What is the best way to do this?
If you have multiple ModelDescriptor objects in a file, in order for the XML file to be valid, you'll have to have a single root element - something like:
<root>
<ModelDescriptor>
....
</ModelDescriptor>
<ModelDescriptor>
....
</ModelDescriptor>
</root>
Basically, you'd create a dummy "container" class which then in turn contains a list of ModelDescriptor objects:
[XmlRoot(Namespace = "", IsNullable = false)]
public class root
{
[XmlElement("ModelDescriptor", Form = XmlSchemaForm.Unqualified)]
public List<ModelDescriptor> Items { get; set; }
}
public class ModelDescriptor
{
public string Model { get; set; }
}
Now you should be able to deserialize your file into an object of type root and get your ModelDescriptors in the Items list:
FileStream fs = new FileStream(#"YourFileNameHere", FileMode.Open, FileAccess.Read);
XmlSerializer ser = new XmlSerializer(typeof(root));
var result = ser.Deserialize(fs); // would be an object of type "root" with the ModelDescriptor inside