How to Find Out When You'Ve Been Loaded via Xml Serialization

How do you find out when you've been loaded via XML Serialization?

Hmmm... it's still not pretty but you could refactor your deserialization logic into a dedicated class which could notify the deserialized object that it originated from XML before returning it to the caller.

Update: I think this should be fairly easy to do without straying too far from the patterns laid by the framework... you'd just need to ensure that you use the CustomXmlSerializer. Classes that need this notification just need to implement IXmlDeserializationCallback

using System.Xml.Serialization;

namespace Custom.Xml.Serialization
{
public interface IXmlDeserializationCallback
{
void OnXmlDeserialization(object sender);
}

public class CustomXmlSerializer : XmlSerializer
{
protected override object Deserialize(XmlSerializationReader reader)
{
var result = base.Deserialize(reader);

var deserializedCallback = result as IXmlDeserializationCallback;
if (deserializedCallback != null)
{
deserializedCallback.OnXmlDeserialization(this);
}

return result;
}
}
}

How to know when an object is currently deserialized from XmlSerializer

I think it is not possible with the current implementation of the Microsoft XmlSerializer.

Execute code to custom type after deserializing an object from Xml

You could make the Object somethingImportant a thread-static variable, and use it in the constructor when non-null, like so:

public class CustomType 
{
[ThreadStatic]
static object deserializationObject;

public static IDisposable SetDeserializationObject(object deserializationObject)
{
return new DeserializationObjectValue(deserializationObject);
}

sealed class DeserializationObjectValue : IDisposable
{
object oldValue;

public DeserializationObjectValue(object value)
{
this.oldValue = deserializationObject;
deserializationObject = value;
}

int disposed = 0;

public void Dispose()
{
// Dispose of unmanaged resources.
if (Interlocked.Exchange(ref disposed, 1) == 0)
{
Dispose(true);
} // Suppress finalization. Since this class actually has no finalizer, this does nothing.
GC.SuppressFinalize(this);
}

void Dispose(bool disposing)
{
if (disposing)
{
// Free any other managed objects here.
deserializationObject = oldValue;
oldValue = null;
}
}
}

private CustomType(object deserializationObject)
{
if (deserializationObject != null)
{
// Handle as needed.
}
}

public CustomType() : this(deserializationObject)
{
}
}

Then set it when deserializing like so:

        using (CustomType.SetDeserializationObject("this is a runtime value"))
{
var serializer = new XmlSerializer(typeof(MyType));
var reader = new StringReader(str);
var myType = serializer.Deserialize(reader) as MyType;
}

By publicly setting the deserializationObject only within the disposable wrapper you ensure it's never left around permanently.

Why does the OnDeserialization not fire for XML Deserialization?

There's no equivalent of OnDeserialized for XML deserialization.

See this post for workarounds: How do you find out when you've been loaded via XML Serialization?

Find out whether property setter is called in DeSerialization process

I have a possible solution.

public class xxx
{
private int myValue;

[XmlElement("MyProperty")]
public int MyPropertyForSerialization
{
get { return this.myValue; }
set
{
Console.WriteLine("DESERIALIZED");
this.myValue = value;
}
}

[XmlIgnore]
public int MyProperty
{
get { return this.myValue; }
set
{
Console.WriteLine("NORMAL");
this.myValue = value;
}
}
}

class Program
{
static void Main(string[] args)
{
xxx instance = new xxx();

instance.MyProperty = 100; // This should print "NORMAL"

// We serialize

var serializer = new XmlSerializer(typeof(xxx));

var memoryStream = new MemoryStream();
serializer.Serialize(memoryStream, instance);

// Let's print our XML so we understand what's going on.

memoryStream.Position = 0;
var reader = new StreamReader(memoryStream);
Console.WriteLine(reader.ReadToEnd());

// Now we deserialize

memoryStream.Position = 0;
var deserialized = serializer.Deserialize(memoryStream) as xxx; // This should print DESERIALIZED

Console.ReadLine();
}
}

The trick is using the XmlIgnore, it will force the xml serializer to ignore our property, then we use XmlElement to rename the property for serialization with the name of the property we want.

The problem with this technique is that you have to expose a public property for serialization, and is in some way bad because it can virtually be called by everyone.
It will not work if the member is private, unfortunally.

It works, is not totally clean, but is thread safe and don't rely on any flag.

Another possibility is to use something like the Memento pattern.
Using the same trick you can add a property called for example Memento that returns another object that contains properties suitable only for serialization, it can makes things a little cleaner.

Did you think instead of changing approach and using DataContractSerializer? It is much more powerful and produces pure XML. It supports the OnDeserializationCallback mechanism.

Serializing a XML Document with 3 Levels

Although IXMLSerializer was an option I ended up creating an extension method instead

public static string Serialize<T>(this object obj)
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
var xmlWriterSettings = new XmlWriterSettings() { Indent = true };
XmlSerializer serializer = new XmlSerializer(typeof(T));
var stringWriter = new StringWriter();
using var writer = XmlWriter.Create(stringWriter, xmlWriterSettings);
serializer.Serialize(writer, obj, ns);
writer.Close();
return stringWriter.ToString();
}

And my main code I could keep the same with [XmlRoot(ElementName = "xml")] but just remove the [XmlElement("JournalEntry")]

This ended up solving my issue.

How do I control the ?xml ? part of xml serialization with .NET?

Try this:

public static string XmlSerialize(object o)
{
using (var stringWriter = new StringWriter())
{
var settings = new XmlWriterSettings
{
Encoding = Encoding.GetEncoding(1252),
OmitXmlDeclaration = true
};
using (var writer = XmlWriter.Create(stringWriter, settings))
{
var xmlSerializer = new XmlSerializer(o.GetType());
xmlSerializer.Serialize(writer, o);
}
return stringWriter.ToString();
}
}

This won't get rid of the xsd: namespace, but then, why do you want to?


Update: It seems that whenever you use a StringWriter, you get UTF-16, even if you use an XmlWriter on top of it with encoding set. Next step would be to write out to a MemoryStream. But that raises the question of why you want to return a string. For instance, if you're going to just turn around and output the string to a stream, then we should output directly to this stream. Same for a TextWriter.



Related Topics



Leave a reply



Submit