Home > other >  Exclude lambda (or delegates) in DataContract
Exclude lambda (or delegates) in DataContract

Time:08-11

I have a scenario where I will receive a dictionary<string, object>(); that can also be recursive though not always, but the main issue is that it contains lambdas.

as we implement a transaction system I need to clone it, which works fine until I hit the lambdas. I tried to move these to delegates but the error changes and still gives me runtime issue.

technically I can re inject the lambdas after the cloning, but don't know how to tell the DataContractSerializer to ignore these.

I also cannot remove the lambdas prior to the cloning as the original object still needs them if I cancel the transaction.

CodePudding user response:

In your Clone() method you can use the data contract surrogate functionality to replace all System.Delegate objects in your serialization graph with a serializable type, such as a lookup in a dictionary of delegates that you build as you serialize. Then, as you deserialize, you could replace the deserialized serialization surrogates with the original delegates.

The following does this:

public static class DataContractExtensions
{
    public static Dictionary<string, object> Clone(this Dictionary<string, object> dictionary)
    {
        if (dictionary == null)
            return null;

        var surrogate = new DelegateSurrogateSelector();

        var types = new[] 
            {
                typeof(Dictionary<string, object>),
                typeof(DelegateSurrogateId),
                // Add in whatever additional known types you want.
            };

        var serializer = new DataContractSerializer(
            typeof(Dictionary<string, object>),
            types, int.MaxValue, false, false,
            surrogate);

        var xml = dictionary.ToContractXml(serializer, null);

        return FromContractXml<Dictionary<string, object>>(xml, serializer);
    }

    public static string ToContractXml<T>(this T obj, DataContractSerializer serializer, XmlWriterSettings settings)
    {
        serializer = serializer ?? new DataContractSerializer(obj == null ? typeof(T) : obj.GetType());
        using (var textWriter = new StringWriter())
        {
            using (var xmlWriter = XmlWriter.Create(textWriter, settings))
            {
                serializer.WriteObject(xmlWriter, obj);
            }
            return textWriter.ToString();
        }
    }

    public static T FromContractXml<T>(string xml, DataContractSerializer serializer)
    {
        using (var textReader = new StringReader(xml ?? ""))
        using (var xmlReader = XmlReader.Create(textReader))
        {
            return (T)(serializer ?? new DataContractSerializer(typeof(T))).ReadObject(xmlReader);
        }
    }

    [DataContract]
    class DelegateSurrogateId
    {
        [DataMember]
        public int Id { get; set; }
    }

    class DelegateSurrogateSelector : IDataContractSurrogate
    {
        public Dictionary<int, System.Delegate> DelegateDictionary { get; private set; }

        public DelegateSurrogateSelector()
        {
            this.DelegateDictionary = new Dictionary<int, Delegate>();
        }

        #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(Delegate).IsAssignableFrom(type))
                return typeof(DelegateSurrogateId);
            return type;
        }

        public object GetDeserializedObject(object obj, Type targetType)
        {
            var id = obj as DelegateSurrogateId;
            if (id != null)
                return DelegateDictionary[id.Id];
            return obj;
        }

        public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
        {
            throw new NotImplementedException();
        }

        public object GetObjectToSerialize(object obj, Type targetType)
        {
            var del = obj as Delegate;
            if (del != null)
            {
                var id = DelegateDictionary.Count;
                DelegateDictionary.Add(id, del);
                return new DelegateSurrogateId { Id = id };
            }
            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
    }
}

Using the above Clone() extension method, the following test will pass:

Func<bool> returnTrue = () => true;
Func<bool> returnFalse = () => false;

var dictionary = new Dictionary<string, object>()
{
    { "a", "hello"},
    { "b", 10101 },
    { "c", returnTrue },
    { "d", new Dictionary<string, object>()
        {
            { "q", 101 },
            { "r", returnFalse },
        }
    }
};

var dictionary2 = dictionary.Clone();

Assert.AreEqual(returnTrue, dictionary2["c"]); // No failure
var inner = (Dictionary<string, object>)dictionary["d"];
var inner2 = (Dictionary<string, object>)dictionary2["d"];

Assert.AreEqual(inner["r"], inner2["r"]);    // No failure

Notes:

  • Related