Copying Data and Intermediate Types
When serializing to GraphML, any serilizable data needs to be represented as a primitive type (a numerical or string type). Furthermore, in order for a property value to be serialized, the QuickGraph GraphML serialization facility requires that the property be decorated with XmlAttribute. Unfortunately, implementing support for GraphML serialization wasn't as easy as just decorating some properties with XmlAttribute since properties of each state are held in the FiniteStateMachine class (start/final state markers).
To address this issue, I created two intermediate types: one to hold all serializable transition data (GraphMLTransition), and the other to hold all serializable state data (GraphMLState). In essence, each of these types represents the <node> and <edge> GraphML elements. Each time the FSM is serialized, it is copied into a new graph of the intermediate serializable types, and then the new graph is serialized. Similarly, when the GraphML is deserialized, the resulting graph is copied into a new FSM.
I could have avoided creating these extra types and implementing the copy process, but to do so would have cluttered and complicated the overall FSM implementation; XML-serializable properties must be public! For states, an actual state class is needed containing the start/final state flags (in essence, the GraphMLState class). Storing start/final state flags on a state class creates a very sparse data set which I wanted to avoid (there is only ever one start state, and generally very few final states), and consequently I chose to maintain the string representation for the state. For transitions, each delegate needs to be represented as a string implying a new read/write property. This new property doesn't make sense as it is unnatural for a user to set a delegate/event using a string representation.
Serializing Delegates (and events)
The TransitionPredicate property and OnStateTransition event are both delegates, which are difficult to serialize/deserialize to/from XML. You can't expect a delegate to be serialized using XmlSerializer becuase an XML-serializable type requires a parameterless constructor. So to accomplish this, delegate-to-string-to-delegate conversion code is required.
A delegate may reference either a static or instance method, and in order to deserialize an instance method, the serializer needs to reconstruct the object state that owns the method. In otherwords, the method's parent object needs to be serialized as well. This does not create a user friendly scenario for creating an FSM via XML, so I prohibited this type of method from being serialized all together. Binary serialization is better suited for this task, which is a feature that will be implemented in the future.
So, for delegate serialization to work, your delegate must reference a static method. When serialized, it will have the following form: methodName;assemblyQualifiedDeclaringTypeName. An example of a serialized delegate for the [System.Char.IsDigit] method is: IsDigit;System.Char, mscorlib, Version=184.108.40.206, Culture=neutral, PublicKeyToken=b77a5c561934e089.
During deserialization, if the method is discovered to be invalid (i.e. it does not exist, the signature isn't that of a predicate, etc...) then it will be replaced with a default predicate. The default predicate returns false for any input value.
Events pose a challenging conversion task because an event is really a multicast delegate in disguise. Each method subscribed to the event will need to be validated and serialized as descibed above. Furthermore, this will be a reflection-heavy task since the delegate is stored in a compiler-generated field. Currently, I have avoided implementing this feature and will consider its implementation in the future.