Why is InvalideOperationException thrown when I try to serialize to XML an ObservableCollection containing hierarchical elements?

心不动则不痛 提交于 2019-12-12 04:07:11

问题


I write WPF MVVM modular application using Prism 6 in MS VS 2015 Professional. When I try to serialize to XML file the hierarchical ObservableCollection using System.Xml.Serialization.XmlSerializer then System.InvalidOperationException is thrown in System.Xml.dll. Below is definition of the collection:

public class Group : ProfileElementType
{
    . . . . . .
    private ObservableCollection<ProfileElementType> _childProfileElenents;
    [Browsable(false)]
    public ObservableCollection<ProfileElementType> ChildProfileElenents
    {
        get { return this._childProfileElenents; }
        set { this_childProfileElenents = value; }
    }
    public Group()
    {
        . . . . .
        this.ChildProfileElenents = new ObservableCollection<ProfileElementType>();
        . . . . .
    }
    . . . . .
}

Where [Browsable(false)] is System.ComponentModel.BrowsableAttribute. And now, let me give further details. Please see below the complete definition of ProfileElementType class and classes which are inherited from it. This is ProfileElementType class:

[XmlInclude(typeof(Group))]
[XmlInclude(typeof(Register))]
public class ProfileElementType : BindableBase, IProfileElementType
{
    #region Fields
    private string _elementTypeName;
    #endregion

    #region Properties
    [Browsable(false)]
    public string ElementTypeName
    {
       get { return this._elementTypeName; }
       set { this._elementTypeName = value; }
    }
    #endregion

    #region IProfileElementType Implementation
    private Type _elementType;
    [Browsable(false)]
    public Type ElementType
    {
       get { return this._elementType; }
       set { this._elementType = value; }
    }
    #endregion
}

Where BindableBase class here is Prism.MVVM.BindableBase. And IProfileElementType interface is shown below:

public interface IProfileElementType
{
   Type ElementType { get; set; }
}

Below is Group and Register classes. These classes are inherited from ProfileElementType class. So this is Group class definition:

public class Group : ProfileElementType
{
    #region Fields
    private string name;
    private string description;
    private ObservableCollection<ProfileElementType> _childProfileElenents;
    private Group parent;
    #endregion

    #region Constructors
    public Group()
    {
       this.ElementType = this.GetType();
       this.ElementTypeName = this.ElementType.Name;
       this.Name = "Group 1";
       this.ChildProfileElenents = new ObservableCollection<ProfileElementType>();
    }
    #endregion

    #region Properties
    [Display(Description = "The Name of this Group.", Name = "The Name")]
    public string Name
    {
        get { return this.name; }
        set { this.SetProperty(ref this.name, value); }
    }
    [Display(Description = "The Brief Description of this Group", Name = "The Description")]
    public string Description
    {
        get { return this.description; }
        set { this.SetProperty(ref this.description, value); }
    }
    /// <summary>
    /// The collection of elements which are the children elements of this group.
    /// These elements can be of Group class instances or Register class instances
    /// </summary>
    [Browsable(false)]
    public ObservableCollection<ProfileElementType> ChildProfileElenents
    {
        get { return this._childProfileElenents; }
        set { this.childProfileElenents = value; }
    }
    /// <summary>
    /// Parent group whose ChildProfileElenents collection comprises this Group instance.
    /// </summary>
    [Browsable(false)]
    public Group Parent
    {
        get { return this.parent; }
        set { this.parent = value; }
    }
    #endregion
}

And now, let me give you the definition of Register class:

public class Register : ProfileElementType
{
   #region Fields
   private int number;
   private string name;
   private string description;
   private string currentValue;
   private string defaultValue;
   private string minimalValue;
   private string maximalValue;
   private string dataTypeName;
   private RegisterDataAccess accessToData;
   private Group parent;
   #endregion

   #region Constructors
   public Register()
   {
       this.ElementType = this.GetType();
       this.ElementTypeName = this.ElementType.Name;
       this.Name = "REgister 1";
       this.AccessToData = RegisterDataAccess.Read_Write;
   }
   #endregion

   #region Properties
   [Display(Description = "Register Address.", Name = "The Address", GroupName = "Common Information")]
   public int Number
   {
       get { return this.number; }
       set { this.SetProperty(ref this.number, value); }
   }
   [Display(Description = "Register Name.", Name = "The Name", GroupName = "Common Information")]
   public string Name
   {
      get { return this.name; }
      set { this.SetProperty(ref this.name, value); }
   }
   [Display(Description = "Breif description.", Name = "The Description", GroupName = "Common Information")]
   public string Description
   {
      get { return this.description; }
      set { this.SetProperty(ref this.description, value); }
   }
   [Display(Description = "Register current value.", Name = "Current Value", GroupName = "Data")]
   public string CurrentValue
   {
      get { return this.currentValue; }
      set { this.SetProperty(ref this.currentValue, value); }
   }
   [Display(Description = "Register default value.", Name = "Default value", GroupName = "Data")]
   public string DefaultValue
   {
      get { return this.defaultValue; }
      set { this.SetProperty(ref this.defaultValue, value); }
   }
   [Display(Description = "Register Minimal Value.", Name = "Minimal Value", GroupName = "Data")]
   public string MinimalValue
   {
      get { return this.minimalValue; }
      set { this.SetProperty(ref this.minimalValue, value); }
   }
   [Display(Description = "Register Maximal Value.", Name = "Maximal Value", GroupName = "Data")]
   public string MaximalValue
   {
      get { return this.maximalValue; }
      set { this.SetProperty(ref this.maximalValue, value); }
   }
   [Display(Description = "Register Data Type.", Name = "Data Type", GroupName = "Data")]
   public string DataTypeName
   {
      get { return this.dataTypeName; }
      set { this.SetProperty(ref this.dataTypeName, value); }
   }
   [Display(Description = "Access to Register Data.", Name = "Data Access", GroupName = "Data")]
   public RegisterDataAccess AccessToData
   {
      get { return this.accessToData; }
      set { this.SetProperty(ref this.accessToData, value); }
   }
   /// <summary>
   /// Parent group whose ChildProfileElenents collection comprises this Register instance.
   /// </summary>
   [Browsable(false)]
   public Group Parent
   {
      get { return this.parent; }
      set { this.parent = value; }
   }
   #endregion
}

Where [Display(Description = "description_string", Name = "name_string")] is from System.ComponentModel.DataAnnotations namespace. Here is RegisterDataAccess enumeration which is the type of accessToData field and AccessToData property in Register class:

public enum RegisterDataAccess
{
   /// <summary>
   /// Access to data in register is read-only.
   /// </summary>
   Read,
   /// <summary>
   /// Access to data in register is write-only.
   /// </summary>
   Write,
   /// <summary>
   /// Read and write access to data in register.
   /// </summary>
   Read_Write
}

Below is serializeProfile generic method definition in DeviceProfileViewModel class. This method executes XML serialization:

public class DeviceProfileViewModel : BindableBase, IConfirmNavigationRequest
{
   . . . . . . .
   private string PathToXmlRepository;
   private string XmlRepositoryFileName = "XmlRepository.XML";
   private Group _profileTreeRoot;
   public Group ProfileTreeRoot
   {
      get { return this._profileTreeRoot; }
      set { this.SetProperty(ref this._profileTreeRoot, value); }
   }
   . . . . . . .
   public DeviceProfileViewModel()
   {
      . . . . . . .
      this.ProfileTreeRoot = new Group();
      this.ProfileTreeRoot.ChildProfileElenents.CollectionChanged += this.ChildProfileElenents_CollectionChanged;
      this.PathToXmlRepository = Path.GetFullPath("../../XML_DeviseProfileRepository");
      . . . . . . .
   }

   /// <summary>
   /// Executes XML serialization.
   /// </summary>
   /// <typeparam name="T">Type of serialized element</typeparam>
   /// <param name="parameter">Serialized element itself</param>
   private void serializeProfile<T>(T parameter)
   {
      string pathToXml = Path.Combine(this.PathToXmlRepository, this.XmlRepositoryFileName);
      if (File.Exists(pathToXml))
         File.Delete(pathToXml);
      XmlSerializer xs = new XmlSerializer(typeof(T));

      using (StreamWriter wr = new StreamWriter(pathToXml))
      {
          xs.Serialize(wr, parameter);
      }
   }
   . . . . . . . .
}

Below is serializeProfile() method call in DeviceProfileViewModel class:

ObservableCollection<ProfileElementType> coll = new ObservableCollection<ProfileElementType>(this.ProfileTreeRoot.ChildProfileElenents);
this.serializeProfile(coll);

I do assignment above because this.ProfileTreeRoot.ChildProfileElenents ObservableCollection is used in data binding as ItemsSource for telerik:RadTreeView in the view. You can see a device profile tree (group and registers) on the picture below:

So this.ProfileTreeRoot here is the instance of Group class. Execution of

xs.Serialize(wr, parameter);

line of code in serializeProfile() method throws InvalideOperationException exception in System.Xml.dll and an empty XML file is created in the specified path. What should I do to eliminate this error? Your help will be greatly appreciated. HELP me please serialize such a collection to XML file!!!

来源:https://stackoverflow.com/questions/37058259/why-is-invalideoperationexception-thrown-when-i-try-to-serialize-to-xml-an-obser

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!