I am using a DataContractJsonSerializer
to serialize an object graph. When I construct the objects, each receives a reference to an instance of a utility object (it's a factory, for creating instances of subclasses of an abstract contract class) - which works great until the graph is serialized and then deserialized again, whereupon the objects no longer have a reference to the utility object any more. I need this reference. How would you recommend I implement this (singletons don't work because separate graphs need their own instance of the object)?

- 2,401
- 4
- 32
- 57
2 Answers
One way to accomplish this is with a data contract surrogate. Using surrogates, you can replace your "real" factory with a dummy stub during serialization. Then, during deserialization, replace the dummy with the desired factory.
Thus, if your classes look something like:
public abstract class FactoryBase
{
}
public class Factory : FactoryBase
{
}
public interface IHasFactory
{
FactoryBase Factory { get; }
}
[DataContract]
public abstract class HasFactoryBase : IHasFactory
{
[DataMember(IsRequired = true)]
FactoryBase factory;
public FactoryBase Factory { get { return factory; } }
public HasFactoryBase(FactoryBase factory)
{
this.factory = factory;
}
}
[DataContract]
public class Foo : HasFactoryBase
{
public Foo(FactoryBase factory)
: base(factory)
{
this.Bars = new List<Bar>();
}
[DataMember]
public List<Bar> Bars { get; set; }
}
[DataContract]
public class Bar : HasFactoryBase
{
public Bar(FactoryBase factory) : base(factory) { }
}
You define an IDataContractSurrogate
to replace all occurrences of FactoryBase
with a surrogate as follows:
public class FactorySurrogateSelector : IDataContractSurrogate
{
[DataContract]
class FactorySurrogate
{
}
readonly FactoryBase factory;
public FactorySurrogateSelector(FactoryBase factory)
{
this.factory = factory;
}
#region IDataContractSurrogate Members
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
throw new NotImplementedException();
}
public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
{
throw new NotImplementedException();
}
public Type GetDataContractType(Type type)
{
if (typeof(FactoryBase).IsAssignableFrom(type))
return typeof(FactorySurrogate);
return type;
}
public object GetDeserializedObject(object obj, Type targetType)
{
if (obj is FactorySurrogate)
return factory;
return obj;
}
public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
{
throw new NotImplementedException();
}
public object GetObjectToSerialize(object obj, Type targetType)
{
if (obj is FactoryBase)
{
return new FactorySurrogate();
}
return obj;
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
throw new NotImplementedException();
}
public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
{
throw new NotImplementedException();
}
#endregion
}
Then, serialize and deserialize as follows:
var factory = new Factory();
var test = new Foo(factory)
{
Bars = { new Bar(factory) },
};
var surrogate = new FactorySurrogateSelector(factory);
var serializer = new DataContractJsonSerializer(test.GetType(), Enumerable.Empty<Type>(), int.MaxValue, false, surrogate, false);
byte[] json;
using (var stream = new MemoryStream())
{
serializer.WriteObject(stream, test);
json = stream.ToArray();
}
Foo test2;
using (var stream = new MemoryStream(json))
{
test2 = (Foo)serializer.ReadObject(stream);
}
if (test2.Factory != test.Factory)
throw new InvalidOperationException();
Notice that the desired factory was passed directly into the constructor of the FactorySurrogateSelector
, then eventually set inside each type that contains instances of the factory type.
The resulting JSON will look like:
{
"factory": {},
"Bars": [
{
"factory": {}
}
]
}
Some qualifications:
Your factory must inherit from some common base class, here
FactoryBase
. The data contract serializers will never serialize an interface member, e.g.IFactory factory
whereIFactory
is your factory interface, even when there is an applicable surrogate.The empty
"factory": {}
objects must appear in the JSON in order for the surrogate to inject the correct "real" factory value during deserialization. Hence the[DataMember(IsRequired = true)]
.

- 104,963
- 20
- 228
- 340
Another way to accomplish is to introduce a thread static or thread local factory object, then populate your classes with it using an [OnDeserializing]
callback.
Thus, if you define your types as follows:
public interface IFactory
{
}
public class Factory : IFactory
{
}
public interface IHasFactory
{
IFactory Factory { get; }
}
[DataContract]
public abstract class HasFactoryBase : IHasFactory
{
[ThreadStatic]
static IFactory deserializedFactory;
static IFactory DeserializedFactory
{
get
{
return deserializedFactory;
}
set
{
deserializedFactory = value;
}
}
public static IDisposable SetDeserializedFactory(IFactory factory)
{
return new PushValue<IFactory>(factory, () => DeserializedFactory, val => DeserializedFactory = val);
}
IFactory factory;
public IFactory Factory { get { return factory; } }
public HasFactoryBase(IFactory factory)
{
this.factory = factory;
}
[OnDeserializing]
void OnDeserializing(StreamingContext context)
{
this.factory = DeserializedFactory;
}
}
public struct PushValue<T> : IDisposable
{
Action<T> setValue;
T oldValue;
public PushValue(T value, Func<T> getValue, Action<T> setValue)
{
if (getValue == null || setValue == null)
throw new ArgumentNullException();
this.setValue = setValue;
this.oldValue = getValue();
setValue(value);
}
#region IDisposable Members
// By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.
public void Dispose()
{
if (setValue != null)
setValue(oldValue);
}
#endregion
}
[DataContract]
public class Foo : HasFactoryBase
{
public Foo(IFactory factory)
: base(factory)
{
this.Bars = new List<Bar>();
}
[DataMember]
public List<Bar> Bars { get; set; }
}
[DataContract]
public class Bar : HasFactoryBase
{
public Bar(IFactory factory) : base(factory) { }
}
You can serialize and deserialize as follows:
var factory = new Factory();
var test = new Foo(factory)
{
Bars = { new Bar(factory) },
};
var serializer = new DataContractJsonSerializer(test.GetType());
byte [] json;
using (var stream = new MemoryStream())
{
serializer.WriteObject(stream, test);
json = stream.ToArray();
}
Foo test2;
using (HasFactoryBase.SetDeserializedFactory(factory))
using (var stream = new MemoryStream(json))
{
test2 = (Foo)serializer.ReadObject(stream);
}
if (test2.Factory != test.Factory)
throw new InvalidOperationException();
And the JSON will look like:
{
"Bars": [
{}
]
}
Some notes:
The factory object does not appear at all in the JSON.
The factory objects no longer need to inherit from some abstract base class, they can simply implement a common
IFactory
interface.

- 104,963
- 20
- 228
- 340