ICollection<IInterface> Serialization problem

Nov 3, 2013 at 5:34 PM
Hello.

I want to serialize to xml my class:
    [Serializable]
    public class Solution : AbstractFolder, ISolution
    {      
        public static readonly PropertyData FoldersProperty =
            RegisterProperty<Solution, IReadOnlyList<IFolder>>(model => model.Folders, new Collection<IFolder>());

        [IncludeInSerialization]
        private ICollection<IFolder> FoldersPrivate
        {
            get { return GetValue<Collection<IFolder>>(FoldersProperty); }
            set { SetValue(FoldersProperty, value); }
        }

        [ExcludeFromSerialization]
        public IReadOnlyList<IFolder> Folders
        {
            get { return GetValue<IReadOnlyList<IFolder>>(FoldersProperty); }
        }
        //.....
}

    public interface IFolder : IModel
    {
        IFolderContainer ParentFolder { get; }
        string Name { get; set; }
        string Path { get; }

        Guid TypeID { get; }
        Guid ID { get; }
    }

    public interface IProject : IFolder, IBuildable
    {
        IReadOnlyList<IItem> Items { get; }

        void AddItem(IItem item);

        void RemoveProjectItem(IItem item);
    }

    [Serializable]
    public abstract class AbstractProject : AbstractFolder, IProject
    {
        /// <summary>Register the Items property so it is known in the class.</summary>
        public static readonly PropertyData ItemsProperty =
            RegisterProperty<AbstractProject, IReadOnlyList<IItem>>(model => model.Items, new Collection<IItem>());

        private ICollection<IItem> ItemsInternal
        {
            get { return GetValue<Collection<IItem>>(ItemsProperty); }
            set { SetValue(ItemsProperty, value); }
        } 

        [ExcludeFromSerialization]
        public IReadOnlyList<IItem> Items
        {
            get { return GetValue<IReadOnlyList<IItem>>(ItemsProperty); }
        }
    }

        [TestMethod]
        public void CreateSolution()
        {
            var sol = SolutionService.CreateSolution("TestSolution", @".\Test");

            var project = new AbstractProject(sol, "TestProject", @".\Test\TestProject");

            sol.AddFolder(project);

            SolutionService.SaveSolution(sol);
        }
I wan't to grant only read only access to filders, but be able to add new element from inside, so i made private property ICollection<IFolder> and publuc IReadOnlyList<IFolder>.
When i'm trying to serialize, there is an error:
System.Runtime.Serialization.SerializationException: There was an error serializing the object of type System.Collections.ObjectModel.Collection`1[[PipeLine.Core.Project.Items.IFolder, PipeLine.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]. The prefix 'd1p1' cannot be redefined from 'http://schemas.microsoft.com/2003/10/Serialization/Arrays' to 'http://schemas.datacontract.org/2004/07/PipeLine.Core.Project' within the same start element tag. ---> System.Xml.XmlException: The prefix 'd1p1' cannot be redefined from 'http://schemas.microsoft.com/2003/10/Serialization/Arrays' to 'http://schemas.datacontract.org/2004/07/PipeLine.Core.Project' within the same start element tag.
Result StackTrace:  
at System.Xml.XmlWellFormedWriter.PushNamespaceExplicit(String prefix, String ns)
   at System.Xml.XmlWellFormedWriter.WriteEndAttribute()
   at System.Xml.XmlWriter.WriteAttributeString(String prefix, String localName, String ns, String value)
   at System.Runtime.Serialization.XmlWriterDelegator.WriteXmlnsAttribute(String ns)
   at System.Runtime.Serialization.XmlWriterDelegator.WriteXmlnsAttribute(XmlDictionaryString ns)
   at System.Runtime.Serialization.XmlWriterDelegator.WriteAttributeQualifiedName(String attrPrefix, XmlDictionaryString attrName, XmlDictionaryString attrNs, XmlDictionaryString name, XmlDictionaryString ns)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteTypeInfo(XmlWriterDelegator writer, XmlDictionaryString dataContractName, XmlDictionaryString dataContractNamespace)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteTypeInfo(XmlWriterDelegator writer, DataContract contract, DataContract declaredContract)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiType(XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle objectTypeHandle, Type objectType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle)
   at WriteArrayOfanyTypeToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , CollectionDataContract )
   at System.Runtime.Serialization.CollectionDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
   at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle)
   at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)
   at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph)
   at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectContentHandleExceptions(XmlWriterDelegator writer, Object graph)
 --- End of inner exception stack trace ---
    at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectContentHandleExceptions(XmlWriterDelegator writer, Object graph)
   at System.Runtime.Serialization.DataContractSerializer.WriteObjectContent(XmlWriter writer, Object graph)
   at Catel.Runtime.Serialization.XmlSerializer.WriteXmlElement(ISerializationContext context, XElement element, String elementName, MemberValue memberValue, Type modelType)
   at Catel.Runtime.Serialization.XmlSerializer.SerializeMember(ISerializationContext`1 context, MemberValue memberValue)
   at Catel.Runtime.Serialization.SerializerBase`1.SerializeMembers(ISerializationContext`1 context, List`1 membersToSerialize)
   at Catel.Runtime.Serialization.SerializerBase`1.Serialize(ModelBase model, TSerializationContext context)
   at Catel.Runtime.Serialization.SerializerBase`1.Serialize(ModelBase model, Stream stream)
If i exclude from serialization this colletion or serialize empthy collection it's serializing perfectly.
As i understand, the problem is that both, Project and Sollution, contain ICollection and they have the same prefix d1p1, how can i solve this problem?
Coordinator
Nov 4, 2013 at 7:34 PM
Can you please create an issue + repro here:

https://catelproject.atlassian.net/secure/Dashboard.jspa
Nov 6, 2013 at 9:46 PM
Hi, here is a repro : https://catelproject.atlassian.net/browse/CTL-226
Also i want to ask, is it ok to create 2 properties with different types, but which access the same PropertyData, to provide read only acces to collection?
Coordinator
Nov 7, 2013 at 1:25 PM
Thanks, we will look into that asap.

"Also i want to ask, is it ok to create 2 properties with different types, but which access the same PropertyData, to provide read only acces to collection?"

Yes, you can do that. However, keep in mind that internally they will use the same value in the propertybag of the object. This means that both will change the same property. You will have to do the casting yourself there.
Dec 19, 2013 at 5:34 PM
Hello Greet.

Again i faced with similar problem.

here is code:
        public static readonly PropertyData PanesProperty = RegisterProperty<WorkspaceService, ICollection<IPane>>(model => model.Panes, () => new FastObservableCollection<IPane>());
        public ICollection<IPane> Panes
        {
            get { return GetValue<ICollection<IPane>>(PanesProperty); }
            private set { SetValue(PanesProperty, value); }
        }
//....
            using (var stream = File.Open(GetLayoutFilePath(State), FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                var xmlSerializer = SerializationFactory.GetXmlSerializer();
                xmlSerializer.OptimalizationMode = XmlSerializerOptimalizationMode.PrettyXml;
                
                xmlSerializer.Deserialize(this, stream);
During deserialization in the log:
[WARNING] [Catel.Runtime.Serialization.Xml.XmlSerializer] Property type for property 'Panes' is 'System.Collections.Generic.ICollection1[[GameEd.Base.UI.Tabs.IPane, GameEd.Base, Version=1.1.5101.39300, Culture=neutral, PublicKeyToken=null]]' but found type info that it should be deserialized as 'Catel.Collections.FastObservableCollection1[[GameEd.Base.UI.Tabs.IPane]]'. Unfortunately the type cannot be found so the deserialization will probably fail.
And Pane property is emthy. May be better throw exception or some how notify about this, becaurse now i passing serialization and trying to restore layout and in the end i have invalid layout.
Also i want to ask how to solve this problem with interfaces?
Dec 21, 2013 at 10:38 PM
Hi.
        public ICollection<IPane> Panes
        {
            get { return GetValue<ICollection<IPane>>(PanesProperty); }
            private set { SetValue(PanesProperty, value); }
        }

        public class PaneCollection : ObservableCollection<IPane>
        {
            protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
            {
                base.OnCollectionChanged(e);
            }
        }
THis solved the problem, but i think it's not good.
Coordinator
Dec 22, 2013 at 5:36 PM
I think the warning is a bit premature because it should be able to find the type. Can you wrap this in a repro and upload it here:

http://www.catelproject.com/support/issue-tracker
Coordinator
Dec 24, 2013 at 7:37 PM
FYI: we have fixed this issue.