Matas Vaitkevicius
Matas Vaitkevicius

Reputation: 61391

DataContractJsonSerializer serialization error: Consider using a DataContractResolver or add any types not known statically to the list

I am extending object that is being serialized with System.Runtime.Serialization.Json.DataContractJsonSerializer with two additional properties that are strings (not a complex types) by inheritance. .Net version used is .NET 4.
Serialization mechanism works fine for the base object but fails for object with two additional properties which seems very peculiar to me.
I am using [DataContract] attributes on both base and inherited object and all properties on both of them have [DataMember] attributes with names.
Both objects are internal but I don't see how this could affect serialization of child object.
While debugging I have observed base object to step in to the try block and get serialized, and child to blow up on line serializer.WriteObject(ms, sourceObject);
Adding known type attribute [KnownType(typeof(OnTheMoveBusinessComponentJavaScriptInitObject))] on inherited object results in same error in same place.
Why can't I substitute base object with inherited one?

ChildObject:

namespace OnTheMoveLibrary.DataControls
{
    [DataContract]
    [KnownType(typeof(OnTheMoveBusinessComponentJavaScriptInitObject))]
    internal class OnTheMoveTreeBusinessComponentJavaScriptInitObject : OnTheMoveBusinessComponentJavaScriptInitObject
    {
        [DataMember(Name = "MasterRecordId")]
        public string MasterRecordId { get; set; }

        [DataMember(Name = "ParentRecordId")]
        public string ParentRecordId { get; set; }
    }
}

BaseObject:

namespace OnTheMoveLibrary.DataControls
{
    [DataContract]
    internal class OnTheMoveBusinessComponentJavaScriptInitObject : OnTheMoveValidatable
    {
        public OnTheMoveBusinessComponentJavaScriptInitObject()
        {
            this.SqlStatementObject = new OnTheMoveSelectStatement();
            this.PreDefaults = new PreDefaultsObject();
            this.ParentAssociations = new List<ParentAssociation>();
            this.CalculatedFields = new List<OnTheMoveCalculatedField>();
            this.BusinessComponentEvents = new List<BusinessComponentEvent>();
        }

        [DataMember(Name = "sqlStatementObject")]
        public IOnTheMoveSelectStatement SqlStatementObject { get; set; }

        [DataMember(Name = "calculatedFields")]
        public List<OnTheMoveCalculatedField> CalculatedFields { get; set; }

        [DataMember(Name = "knockoutContextName")]
        public string KnockoutContextName { get; set; }

        [DataMember(Name = "observable")]
        public bool Observable { get; set; }

        [DataMember(Name = "integrationObjectNameForNewRecords")]
        public string IntegrationObjectNameForNewRecords { get; set; }

        [DataMember(Name = "singleRecordNewFlag")]
        public bool SingleRecordNewFlag { get; set; }

        [DataMember(Name = "recordIndex")]
        public int? RecordIndex { get; set; }

        [DataMember(Name = "primaryTableName")]
        public string PrimaryTableName { get; set; }

        /// <summary>
        /// The index within the query string of the "RecordId" parameter to use as a parent to insert new records, defaulting to 0
        /// For example, if we have a recordid of "A123,B123" in the querystring, and set ParentQSRecordIdIndex=1, then B123 is used as the parent object when saving
        /// </summary>
        [DataMember(Name = "parentRecordIdQueryStringIndex")]
        public int? ParentRecordIdQueryStringIndex { get; set; }

        [DataMember(Name = "parentAssociations")]
        public List<ParentAssociation> ParentAssociations { get; set; }

        [DataMember(Name = "applyBindings")]
        public bool ApplyBindings { get; set; }

        [DataMember(Name = "PreDefaults")]
        public PreDefaultsObject PreDefaults { get; set; }

        /// <summary>
        /// Gets or sets a list of <see cref="BusinessComponentEvent">BusinessComponentEvents</see>.
        /// </summary>
        [DataMember(Name = "businessComponentEvents")]
        public List<BusinessComponentEvent> BusinessComponentEvents { get; set; }

        [DataMember(Name = "automaticLeadingWildcards")]
        public bool? AutomaticLeadingWildcards { get; set; }

        [DataMember(Name = "automaticTrailingWildcards")]
        public bool? AutomaticTrailingWildcards { get; set; }

        [DataMember(Name = "enableAggregateFields")]
        public bool? EnableAggregateFields { get; set; }

        public override void ValidateProperties()
        {
            this.ValidateProperty("SqlStatementObject", this.SqlStatementObject != null ? this.SqlStatementObject.ToString() : null);
            this.SqlStatementObject.ValidateProperties();
        }
    }
}

Serialization Mechanism:

public static string ObjectToJson<TValue>(TValue sourceObject)
    {
        string result = null;
        Type type = typeof(TValue);

        if (type == typeof(object))
        {
            return CallObjectToJsonWithSpecificType(sourceObject);
        }

        Type[] knownTypes = new[] { typeof(OnTheMoveSelectStatement), typeof(OnTheMoveCustomSelectStatement) };
        var serializer = new DataContractJsonSerializer(type, knownTypes);
        var ms = new MemoryStream();
        try
        {
            serializer.WriteObject(ms, sourceObject);
            result = Encoding.UTF8.GetString(ms.ToArray());
        }
        finally
        {
            ms.Close();
        }

        return result;
    }

Upvotes: 1

Views: 1929

Answers (1)

Matas Vaitkevicius
Matas Vaitkevicius

Reputation: 61391

Figured it out myself.

Instead of adding base class type in attribute to the class that inherits

[DataContract]
[KnownType(typeof(OnTheMoveBusinessComponentJavaScriptInitObject))]
internal class OnTheMoveTreeBusinessComponentJavaScriptInitObject : OnTheMoveBusinessComponentJavaScriptInitObject

one has to do the opposite - add to base class attribute with child class type (which breaks all the Object Oriented design since base class should never know about who inherits from it).

[DataContract]
[KnownType(typeof(OnTheMoveTreeBusinessComponentJavaScriptInitObject))]
internal class OnTheMoveBusinessComponentJavaScriptInitObject : OnTheMoveValidatable

Upvotes: 1

Related Questions