List of Objects Serialization xml fault - c#

I am making a WPF application in which I save list of object on exit of my WPF Application. And get the list of objects on system startup. Everything works fine initially. But Some times it gives the serialization Exception. After getting the exception I looked of the xml serialized file. But it seem to me that the exception was thrown because the xml file was not formed properly. When I corrected it. It again worked fine.
public static class IsolatedStorageCacheManager<T>
{
public static void store(T loc)
{
IsolatedStorageFile appStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly | IsolatedStorageScope.Domain, null, null);
using(IsolatedStorageFileStream fileStream=appStore.OpenFile("myFile21.xml",FileMode.OpenOrCreate))
{
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
serializer.WriteObject(fileStream, loc);
}
}
public static T retrieve()
{
T obj = default(T);
IsolatedStorageFile appStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly | IsolatedStorageScope.Domain, null, null);
if (appStore.FileExists("myFile21.xml"))
{
using (IsolatedStorageFileStream fileStream = appStore.OpenFile("myFile21.xml", FileMode.Open))
{
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
try
{
obj = (T)serializer.ReadObject(fileStream);
}
catch (SerializationException e)
{
Console.WriteLine(e.StackTrace);
}
}
}
return obj;
}
}

The first thing to do is make sure that the objects passed to store are of a type supported by the DataContractSerializer.
The easiest thing to do is check all store calls yourself.
You could also create a validation method or even better, see if anyone else has implemented one. This method could validate the loc object and return a boolean and be called at the beginning of the store method inside a System.Diagnostics.Debug.Assert call so that it will only run on a debug configuration. Note though that this method could be quite tricky because you will have to validate type T for all the cases mentioned in the specification of DataContractSerializer and if T is a generic validate T's parameter(s) as well.

Related

XML deserialize brings back empty list

I have a dictionary of abilityobjects <id, abilityObj> that I'm trying to serialize in XML. Because you can't XML Serialize a dictionary, I change it into a list on serialization
public class BattleSerializable : TyrantSerializable
{
[XmlIgnoreAttribute]
[NonSerialized]
[DoNotSerialize]
public Dictionary<int, AbilityObjectSerializable> battleObjects;
public List<AbilityObjectSerializable> serializedBattleObjects
{
get
{
if (battleObjects == null)
{
battleObjects = new Dictionary<int, AbilityObjectSerializable>();
}
return battleObjects.Select(x => x.Value).ToList();
}
set
{
battleObjects = value.ToDictionary(x => x.entityId, x => x);
}
}
It serializes correctly. I.e. the XML that gets saved makes sense
<BattleSerializable>
...
<serializedBattleObjects>
<AbilityObjectSerializable xmlns:d3p1="http://www.w3.org/2001/XMLSchema-instance" d3p1:type="FireballObject">
<hexDirection>southeast</hexDirection>
<gridX>0</gridX>
<gridZ>7</gridZ>
<entityId>3</entityId>
<lastAnimation>STATUE</lastAnimation>
<timer>0</timer>
<abilityPos>2</abilityPos>
<abilityType>FIREBALL</abilityType>
<health>100</health>
<tilesPerTurn>2</tilesPerTurn>
<jump>1</jump>
<fall>99</fall>
<damage>5</damage>
<lineTraversed>
<xDisplace>1</xDisplace>
<zDisplace>-2</zDisplace>
<endTileFacing>east</endTileFacing>
</lineTraversed>
<moveDirection>
<xDisplace>1</xDisplace>
<zDisplace>-2</zDisplace>
<endTileFacing>east</endTileFacing>
</moveDirection>
</AbilityObjectSerializable>
</serializedBattleObjects>
</BattleSerializable>
But when I try to then -load- this XML and turn it into actual C# objects, this list comes in empty for some reason, causing the app to blow up.
What am I missing? All the other lists in this class serialize/deserialize correctly.
My load code:
public BattleSerializable Load(string path)
{
var serializer = new XmlSerializer(typeof(BattleSerializable));
try
{
using (var stream = new FileStream(path, FileMode.Open))
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(stream);
string xmlString = xmlDoc.InnerXml;
BattleSerializable bs = (BattleSerializable)this.LoadFromXML(xmlString);
return bs;
}
}
catch (Exception e)
{
throw new SettingLoadException("Settings failed validation");
}
}
The way a lot of serializers work is by calling Add on a list, only actually assigning anything back to the setter if the serializer created the list (perhaps because it was null, or fixed size such as an array). So imagine the serializer doing:
var list = obj.SomeProperty;
while (moreOfTheSame)
list.Add(ReadOneOfThose());
It never calls the setter, so any logic in there: irrelevant. You'll probably need a custom list type here, or perhaps more simply: have a nice simple POCO/DTO model that just maps to the serialization shape with no fun logic, and project between this model and your domain model separately to serialization.

Inherited XmlTextWriter with XmlWriterSettings

There are quite a few questions related to this topic that I have browsed through.
But it seems that I have a unique case where we are inheriting a class from XmlTextWriter which makes it impossible to use the XmlWriter.Create() method for instantiating an XmlWriter with XmlWriterSettings.
So the question - Is there a way to specify XmlWriterSettings like OmitXmlDeclaration, DoNotEscapeUriAttributes and CloseOutput for the inherited instance ?
Note: I have used Formatting property in the inherited class but could not locate above mentioned properties unless set through XmlWriterSettings.
XmlTextWriter does not support all the options available in XmlWriterSettings. The class was originally created for writing XML in .Net 1.x and was deprecated in favor of XmlWriter.Create() in .Net 2.0, as explained in the docs:
Starting with the .NET Framework 2.0, we recommend that you use the System.Xml.XmlWriter class instead.
Full support for XmlWriterSettings was never added to the old XmlTextWriter and vice versa. This can be confirmed by checking the reference source.
For instance, for CloseOutput, if you look at the reference source for XmlTextWriter.Close() the underlying text writer is closed unconditionally:
public override void Close() {
try {
AutoCompleteAll();
}
catch { // never fail
}
finally {
this.currentState = State.Closed;
textWriter.Close();
}
}
Compare with XmlUtf8RawTextWriter.Close() (this class is one of the XML writers returned by XmlWriter.Create()) where the underlying text writer is conditionally closed:
public override void Close() {
try {
FlushBuffer();
FlushEncoder();
}
finally {
// Future calls to Close or Flush shouldn't write to Stream or Writer
writeToNull = true;
if ( stream != null ) {
try {
stream.Flush();
}
finally {
try {
if ( closeOutput ) {
stream.Close();
}
}
finally {
stream = null;
}
}
}
}
}
(However, you could always construct a StreamWriter that does not close the underlying stream, using one of the answers from Is there any way to close a StreamWriter without closing its BaseStream?.)
Similarly XmlTextWriter.WriteStartDocument() does not appear to have an option to not emit an XML declaration:
public override void WriteStartDocument() {
StartDocument(-1);
}
// Writes out the XML declaration with the version "1.0" and the standalone attribute.
public override void WriteStartDocument(bool standalone) {
StartDocument(standalone ? 1 : 0);
}
void StartDocument(int standalone) {
try {
if (this.currentState != State.Start) {
throw new InvalidOperationException(Res.GetString(Res.Xml_NotTheFirst));
}
this.stateTable = stateTableDocument;
this.currentState = State.Prolog;
StringBuilder bufBld = new StringBuilder(128);
bufBld.Append("version=" + quoteChar + "1.0" + quoteChar);
if (this.encoding != null) {
bufBld.Append(" encoding=");
bufBld.Append(quoteChar);
bufBld.Append(this.encoding.WebName);
bufBld.Append(quoteChar);
}
if (standalone >= 0) {
bufBld.Append(" standalone=");
bufBld.Append(quoteChar);
bufBld.Append(standalone == 0 ? "no" : "yes");
bufBld.Append(quoteChar);
}
InternalWriteProcessingInstruction("xml", bufBld.ToString());
}
catch {
currentState = State.Error;
throw;
}
}
void InternalWriteProcessingInstruction(string name, string text) {
textWriter.Write("<?");
ValidateName(name, false);
textWriter.Write(name);
textWriter.Write(' ');
if (null != text) {
xmlEncoder.WriteRawWithSurrogateChecking(text);
}
textWriter.Write("?>");
}
It would seem StartDocument() must needs be called to initialize the writer's internal state, but when called, an XML declaration is always written.
As an alternative, have you considered using the decorator pattern and wrapping the writer returned from XmlWriter.Create() in a decorator, e.g. as is shown in this answer to How can I stop empty XML elements self-closing using XmlDocument in C#? This would allow you to customize your output before passing it to the underlying XmlWriter similarly to how your subclass of XmlTextWriter could customize the output before passing to the base class's methods.

Weird situation with lock field being null - c# probably .net 4.0 win8-32 specific

I have the following code:
public class Settings
{
private object _lock = new object();
public void Save() {
lock (_lock)
{
...
}
}
}
On windows 8 x86 / .net 4.0 lock throws exception:
Exception Type: System.ArgumentNullException
Exception Message: Value cannot be null.
Exception Target Site: ReliableEnter
I attached debugger and its null. _lock is really null :|
On windows 7 x64 it works all right. I am checking other OSes now.
EDIT:
It is realted to deserialization. After deserialization of Settings class _lock is null. _lock field didn't exist at the time of serilization of settings and its getting back deserialized as null. Deleting serilized object file and recreating it with lock field eliminated exception. I will check if this is correct deserialization behavior if field didin't exist at serialization. Overwriting object value initialized in declaration doesn't look cool to me. But thats pretty much answer to this. I admit that I didn't consider serialization, deserialization at the time I asked for help ;).
EDIT2:
Here is the code that ilustrates my scenario and what was happening to me:
Serilize class without _lock field
class Program
{
static void Main(string[] args)
{
Settings set = new Settings();
using (FileStream fs = new FileStream(#"C:\test\tst.set", FileMode.Create, FileAccess.Write, FileShare.None))
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs, set);
fs.Flush();
fs.Close();
}
}
}
[Serializable]
internal class Settings
{
private int _i = 12;
public void testMethod()
{
int i = 0;
i++;
Console.WriteLine(i);
}
}
Deserialize to class with lock field and call testMethod:
class Program
{
static void Main(string[] args)
{
Settings set = null;
using (FileStream fs = new FileStream(#"C:\test\tst.set", FileMode.Open, FileAccess.Read, FileShare.Read))
{
BinaryFormatter bf = new BinaryFormatter();
set = (Settings)bf.Deserialize(fs);
fs.Flush();
fs.Close();
}
set.testMethod();
}
}
[Serializable]
internal class Settings
{
private int _i = 12;
private readonly object _lock = new object();
public void testMethod()
{
lock (_lock)
{
int i = 0;
i++;
Console.WriteLine(i);
}
}
}
program will crash on testMethod call.
I didin't find anything related to this behavior in documentaion. I will check for null after deserialization and that will end my trouble.
Ok. How deserilization can assign null to my readonly field assigned on declaration?
To all intents and purposes, if you set it to a new Object and you don't change it anywhere else, then what you're seeing is impossible.
First thing I'd try is to move the new Object code into my constructor, if it's happy there, then leave it be. Either way, trace over the assignment and see if it ever got assigned.
Make sure your IDE is not watching anything that could effect the value of the variable as a side effect - check conditional breakpoints for the same thing.
Note that you can create a conditional break point on change!
If still broken add asserts, compile in debug - run outside of the IDE.
If the debug version crashes when the IDE is not part of the mix, take it to other PCs and try it.
If it works on other PCs, Virus scan/reinstall etc. Memory check etc.
The compiler could be broken, try reinstalling Visual Studio.
Then I'm out of ideas!
Private properties are not serialized by default. If you want them to be serialized you need to create your custom serializer methods.
You can look at the following question for extra details

Binary serialization of mutable F# record

I have used binary serialization to save an F# record from a C# class. All works fine:
F#:
type GameState =
{
LevelStatus : LevelStatus
Grid : Variable<Option<Ball> [,]>
...
}
let game_state : GameState = aGameState()
C#:
public void OnSaveGame() {
using (var stream = File.Open("game_status.sav", FileMode.Create))
{
var binary_formatter = new BinaryFormatter();
binary_formatter.Serialize(stream, PuzzleBobble.game_state);
}
}
Now, I'm refactoring my F# module and I would like to have a mutable record to serialize:
let mutable game_state = aGameState()
game_state <- myGameState()
This way the file is created but when I try to deserialize it I get a null object.
I've changed nothing of my previous implementation, except the added mutable keyword.
My question is: is there anything wrong with serialization of a mutable F# record? Or serialization it self is right, and I have to look for a different error somewhere else in my code?
EDIT:
Even accessing the record through methods like suggested #Brian, it seems not to work.
Here's some more details. When I deserialize the previous saved object this way (that works without game_state declared mutable):
public void OnLoadGame() {
using (var stream = File.Open("game_status.sav", FileMode.Open))
{
var binary_formatter = new BinaryFormatter();
try
{
GameLogic.GameState state = binary_formatter.Deserialize(stream) as GameLogic.GameState;
GameLogic.load_game_state(state);
}
catch (ArgumentNullException e) {
Console.WriteLine(e.Message);
}
}
}
I get the following exception:
'System.ArgumentNullException' in FSharp.Core.dll
I have once seen some weird bug where module-scoped mutable variables in F# libraries were not properly initialized, is it possible you're hitting that?
If you change the code to define
let getGameState() = game_state
let setGameState(x) = game_state <- x
and then use the get/set functions, instead of referring to the mutable variable directly, does the problem go away? If so, this might be an exotic compiler bug that we know about.

Deserialization of optional fields from BinaryFormatter

I have an application that serializes data using BinaryFormatter. A member was added to the class that was serialized from one version to the next without changing the class name. Code was added to handle the possible absence of the added member in old serialized files:
private void readData(FileStream fs, SymmetricAlgorithm dataKey)
{
CryptoStream cs = null;
try
{
cs = new CryptoStream(fs, dataKey.CreateDecryptor(),
CryptoStreamMode.Read);
BinaryFormatter bf = new BinaryFormatter();
string string1 = (string)bf.Deserialize(cs);
// do stuff with string1
bool bool1 = (bool)bf.Deserialize(cs);
// do stuff with bool1
ushort ushort1 = (ushort)bf.Deserialize(cs);
// do stuff with ushort1
// etc. etc. ...
// this field was added later, so it may not be present
// in the serialized binary data. Check for it, and if
// it's not there, do some default behavior
NewStuffIncludedRecently newStuff = null;
try
{
newStuff = (NewStuffIncludedRecently)bf.Deserialize(cs);
}
catch
{
newStuff = null;
}
_newStuff = newStuff != null ?
new NewStuffIncludedRecently(newStuff) :
new NewStuffIncludedRecently();
}
catch (Exception e)
{
// ...
}
finally
{
// ...
}
}
The point I'm at now is that I'd really like to just rinse and repeat with another member I'd like to add, which would mean I'd add another field and try-catch block similar to that for NewStuffIncludedRecently.
I had thought of just making the entire class [Serializable] but wouldn't that break compatibility with the old serialized data?
My main concern is that I'm not clear how the deserialization works. If I add in handling for another optional field similarly to above, will it work? What are other options I have for handling these changes better?
Thanks in advance as always.
If you mark the new fields with [OptionalField] it should work, but I have heard reports of flakiness in some cases. I can't say for sure, since I avoid BinaryFormatter, because it has so many issues when versioning :) (plus, it isn't as "tight" as some alternatives, and has severe issues if you want to go cross-platform, or to CF/SL etc)
If you are implementing ISerializable, you might try:
foreach(SerializationEntry entry in info) {
switch(entry.Name) {
case "Name": Name = (string)info.Value;
case "Id": Id = (int)info.Value;
...
}
}
But again, must stress - this is doing things the hard way :p
With this approach, you only process the data that is actually there.

Categories

Resources