1

I want do some manipulation on all instances of a CustomType that got created with an XmlSerializer.Deserialize

The best way would be to pass in as a constructor, but "PostSerialization" would work just fine. In this example, I need to pass in an object to my CustomType based from the XmlSerializer context.

public class CustomType : IXmlSerializable
{
    public CustomType()
        : this(null)
    {
    }

    public CustomType(Object somethingImportant)
    {
    }

    public void ReadXml(System.Xml.XmlReader reader) { ... }
    public void WriteXml(System.Xml.XmlWriter writer) { ... }
}

...

Object somethingImportant = 12345; // I need to pass this to *all* CustomType
var serializer = new XmlSerializer(typeof(MyType));
var reader = new StringReader(str);
return serializer.Deserialize(reader) as MyType;

XmlAttributeOverrides is a good idea, but "MyType" is quite complex, I can't go through all the possible XmlElement to custom create the CustomType.

jsgoupil
  • 3,788
  • 3
  • 38
  • 53
  • What's the relationship between `CustomType` and `MyType`? – CoderDennis Apr 03 '15 at 19:18
  • @CoderDennis some child of children somewhere in MyType. Example class MyType { public CustomType A {get;set;} }, but more complex. (See last sentence of my question) – jsgoupil Apr 03 '15 at 19:19
  • OK, but I still don't really know what you're looking for. We can't help you tackle your complexity if you don't share any of it with us. – CoderDennis Apr 03 '15 at 19:28
  • I need to pass 12345 to all instances of CustomType before the last line of the code above is being returned. – jsgoupil Apr 03 '15 at 19:45
  • What are you doing in the `ReadXml` and `WriteXml` methods? I haven't seen the need to implement `IXmlSerializable` when doing xml serialization. What is that giving you? – CoderDennis Apr 03 '15 at 20:10
  • @CoderDennis I think we are going away from the question. I believe I'll have to go a different route based on this: http://stackoverflow.com/questions/1266547/how-do-you-find-out-when-youve-been-loaded-via-xml-serialization – jsgoupil Apr 03 '15 at 20:15
  • Was just going to link to this related question: http://stackoverflow.com/questions/2897166/why-does-the-ondeserialization-not-fire-for-xml-deserialization – dbc Apr 03 '15 at 20:15
  • You can use `OnDeserializedAttribute` with `DataContractSerializer`: http://stackoverflow.com/questions/13501525/is-ondeserializedattribute-supported-by-every-serializer – dbc Apr 03 '15 at 20:19
  • Other than that, all I can think to do with `XmlSerializer` is, in the constructor, add all the newly created `CustomType` classes to a thread-static list (assuming it has been allocated, which it would be only during XML deserialization), then clean the list up afterwards. – dbc Apr 03 '15 at 20:20
  • @jsgoupil the line "you could implement IXmlSerializable, but that is lots of work, and very error-prone" in one of the answers to the question you linked to is what I was thinking. – CoderDennis Apr 03 '15 at 20:21
  • 1
    Actually, could you just make `Object somethingImportant` [thread-static](https://msdn.microsoft.com/en-us/library/system.threadstaticattribute%28v=vs.110%29.aspx), and use it when non-null in the parameterless constructor? – dbc Apr 03 '15 at 20:22
  • 1
    @dbc This is a ugly/great solution dbc. Can you propose it as a solution below. I will go with that solution. – jsgoupil Apr 03 '15 at 20:35

1 Answers1

1

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.

dbc
  • 104,963
  • 20
  • 228
  • 340
  • 1
    You went with the full blown example! Thanks! Most of my fields are private/internal, so I simplified a bit. But the example is awesome, thanks. – jsgoupil Apr 03 '15 at 21:42