Fun with DataContractSerializer

I’ve got a complex data structure where some of the child nodes require references to their parent.

DataContractSerializer in C# is meant to handle this. So far as I can tell, enabling support for this has two components:
1. Mark each class which needs to have objects referenced rather than copied with [DataContract(IsReference=true)].
2. When creating DataContractSerializer objects to (de)serialize the object, make sure that preserveObjectReferences = true.

I have done both of these things and the code compiles fine. I can serialise fine, and manual inspection reveals that all data is in fact present and accounted for.

However, I get the following exception when trying to deserialize:

When looking at the generated XML an object with reference ID ‘i1’ absolutely does exist, and it is the parent object of the items which can not find it.

My first thought is that it’s because deserialization occurs child first and, as such, the parent object doesn’t yet exist when the child object’s reference is being set. But this is unavoidable regardless of which one gets set first, so I can’t see why that would be the problem.

Does anyone have experience with this? It’s my first time using a DataContractSerializer, and all of the docs and examples I can find seem to be telling me I’m doing it right. Others who’ve had this problem and “solved” it did so by doing things which I can’t do, such as like removing the DataContract attribute, and weren’t entirely sure why what they did helped at all.

Any pointers as to how to debug this would rock. Cheers!

Update: Some XML output:

<Procedure xmlns:i="..." z:Id="i1" xmlns:z="..." xmlns="...">
	<procedureName>Unnamed Procedure</procedureName>
	<steps>
		<ProcedureStep z:Id="i2">
			<procedure z:Ref="i1" />
			<stepName>Unnamed step</stepName>
		</ProcedureStep>
	</steps>
</Procedure>

It’s the reference “i1” from line 5 to line 1 which breaks.

The serialization/deserialization code:

public static Procedure LoadProcedureFromFile(string path) {
		try {
			System.IO.FileStream fileStream = new System.IO.FileStream(path, System.IO.FileMode.Open, System.IO.FileAccess.Read);
			DataContractSerializer serializer = new DataContractSerializer(typeof(Procedure), null, 1000, false, true, null);
			Procedure procedure = serializer.ReadObject(fileStream) as Procedure;
			fileStream.Close();
			return procedure;
		} catch (System.Exception e) {
			Debug.LogError("Procedure: Load failed with message: " + e.Message);	
		}
		return null;	
	}
	
	public static bool SaveProcedureToPath(Procedure procedure, string path) {
		try {
			
			System.IO.FileStream fileStream = new System.IO.FileStream(path, System.IO.FileMode.Create);
			DataContractSerializer serializer = new DataContractSerializer(typeof(Procedure), null, 1000, false, true, null);
			serializer.WriteObject(fileStream, procedure);
			fileStream.Close();
			return true;
		} catch (System.Exception e) {
			Debug.LogError("Procedure: Save failed with message: " + e.Message);	
			return false;
		}
	}

Aside from that there really isn’t much to it. Two class definitions each with [DataContract(IsReference=true)], and a bunch of properties / variables marked with [DataMember].

Update 2:
Having read that there’s a potential for issues, I replaced all generics in the serializable interface with arrays, and that didn’t help.

I’m stumped.

Well, for now I’ve just ended up writing dirty hacks to get around it. I’m just skipping the parent reference serialization and, after deserialization, walking the tree to fill them in manually.