views:

57

answers:

4

I have a class with a number of fields which are normally calculated in the constructor from other data in the class. They are not serialized to XML because any changes to the rest of the data will likely require their recalculation.

Is there a way I can set up a function call to be triggered on deserialization?

+3  A: 

What you are describing is [OnDeserialized]

XmlSerializer does not support serialization callback methods (at least, not in MS .NET; mono may be different). Depending on your needs, you could try DataContractSerializer which does support serialization callbacks (as do a number of other serializers). Otherwise, your best approach may be to just have your own public method that you call manually.

Another option is to manually implement IXmlSerializable, but this is hard.

Marc Gravell
This is what I was looking for. Frustrating it doesn't work with XML though. I think I'll just make a note to call the method manually. It's just easier.
Tom Savage
A: 

-- Edit:

Just confirmed that, as the commenter below says, the xml serialisation process doesn't hit the 'OnDeserialized'-attribute-declared method. Which is a shame.

-- Previous response:

Yes indeed, take a look here.

Specifically of interest would be the OnDeserialized attribute, discussed here.

It may be worth noting that, depending on the serialization method used (I think) your no-arg constructor will be called. Obviously, this will happen before anything else is set. So it's probably not entirely useful.

Noon Silk
...which isn't used by `XmlSerializer`.
Marc Gravell
@Marc Interesting, why? It doesn't mention that in the documention ...
Noon Silk
It also doesn't mention that it *does* support them ;-p
Marc Gravell
@Marc ... Yes, but seriously though, do you know why?
Noon Silk
Probably because serialization callback were added in 2.0, and XmlSerializer dates from the old 1.1 days. *In theory* it could have introduced breaking changes to existing code (although I guess they could have made it an explicit "UseCallbacks = true" in `[XmlType]`). Most likely, though - they didn't have time to fully evaluate the possible impact, implement it, debug it, etc for the limited users who need it.
Marc Gravell
Oh, and note that the *primary* use of callbacks passes an interface that makes no sense to xml-serializer, adding more confusion.
Marc Gravell
@Marc Fair enough. ( Well, not really fair enough, but it's not like it was your decision :P )
Noon Silk
@Tom - indeed. I'm actually half tempted to see if I can tweak protobuf-net so that it can write to `XmlWriter`, since it *does* support things like callbacks. At the moment the only output it binary (the "protocol buffers" binary format), but I reckon I could make it do something more flexible... xml, json, etc.
Marc Gravell
+1  A: 

Since an object that can be XML serialized need a public parameterless constructor, it seems you have a hole in your class design even before you hit XML serialization.

Personally I would go with lazy calculation of those fields. Store a flag inside the class whether you have calculated the fields or not, and set that field to a value signifying "out of date" when any of the properties that are used in the calculation is changed. Then, in the properties that return the calculated values, check if you need to recalculate before returning the value.

This would then work regardless of XML serialization or not.

example:

[XmlType("test")]
public class TestClass
{
    private int _A;
    private int? _B;

    public TestClass()
        : this(0)
    {
    }

    public TestClass(int a)
    {
        _A = a;
    }

    [XmlAttribute("a")]
    public int A
    {
        get { return _A; }
        set { _A = value; _B = null; }
    }

    [XmlIgnore]
    public int B
    {
        get { if (_B == null) Recalculate(); return _B; }
        set { _B = value; }
    }

    private void Recalculate()
    {
        _B = _A + 1;
    }
}
Lasse V. Karlsen
I do actually have a parameterless constructor but it doesn't really do anything useful because there is no data yet. I would also imagine it would be called before deserialization so I can't do much in there. I'll look into your suggestion though.
Tom Savage
A: 

You can implement IDeserializationCallback interface

alek.sys
See my comments to @silky; `XmlSerializer` doesn't support serialization callbacks (not via the interfaces, and not via the attributes)
Marc Gravell