2017-09-19 4 views
0

J'ai rencontré plusieurs questions et réponses ici mais aucune spécifique à ma situation.Xml Liste Sérialisation et noms de type de noeud

J'ai une classe 'Entity' avec plusieurs classes qui en découlent. Je veux que la sérialisation frappe la liste et comprenne et utilise le type de chaque élément pour le nom de noeud. Maintenant, je peux utiliser ce qui est commenté (définissez chaque élément du tableau dans la classe principale et définissez le nom de celui-ci en utilisant [XmlArrayItem ("Subclass1", typeof (subclass1)] mais je veux conserver toutes les définitions dans leur sous-classe et je vais avoir trop de sous-classes pour définir tout dans la classe d'entité principale ... Y at-il de toute façon pour y parvenir?

J'ai essayé d'utiliser [XmlType (TypeName = "...")] pour le sous-classes et ainsi de suite, mais cela ne fonctionne pas.

[Serializable] 
[XmlInclude(typeof(Subclass1))] 
[XmlRoot("Entity")] 
public class Entity{ 

    [XmlArray("CausedBy")] 
    //[XmlArrayItem("Subclass1", typeof(subclass1))] 
    //[XmlArrayItem("Sublcass2", typeof(Subclass2))] 
    public List<Entity> CausedBy { get; set; } 

} 

[Serializable] 
[XmlRoot("Subclass1")] 
[XmlInclude(typeof(Subclass2))] 
public class Subclass1:Entity{ 
    //Code... 
} 

[Serializable] 
[XmlRoot("Subclass2")] 
public class Subclass2:Subclass1{ 
    //Code... 
} 

Sérialisation le code ci-dessus après la création d'une entité et l'ajout d'un Subclass1 et Subclass2 à la liste Parce que les résultats de la classe « DBY dans ce qui suit:

<Entity> 
    <CausedBy> 
    <Entity ... xsi:type="SubClass1" /> 
    <Entity ... xsi:type="SubClass2" /> 
    </CausedBy> 
<Entity> 

Je voudrais la sortie à afficher:

<Entity> 
     <CausedBy> 
     <SubClass1 .../> 
     <SubClass2 .../> 
     </CausedBy> 
    <Entity> 

Répondre

1

Depuis que j'ai totalement échoué à lire le question pour commencer, voici une nouvelle réponse (c'est un peu un tl; dr, donc vous pouvez toujours passer à la fin et suivez le lien):

Il n'est pas possible d'obtenir la classe de sérialiseur intégrée à travailler parce que vous ne souhaitez pas ajouter les attributs dont il a besoin pour fonctionner. Votre seule option est de sérifier la classe vous-même, cependant, cela ne doit pas être aussi fastidieux que cela puisse paraître; J'ai eu un problème similaire il y a quelques années avec DataGridView en mode virtuel et j'ai produit un virtualiseur générique qui pouvait être utilisé pour virtualiser les données à afficher; il a utilisé un attribut personnalisé:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] 
public sealed class showColumnAttribute : System.Attribute 
{ 
    ///<summary>Optional display format for column</summary> 
    public string Format; 
    ///<summary>Optional Header string for column<para>Defaults to propety name</para></summary> 
    public string Title; 
    ///<summary>Optional column edit flag - defaults to false</summary> 
    public bool ReadOnly; 
    ///<summary>Optional column width</summary> 
    public int Width; 
    ///<summary> 
    ///Marks public properties that are to be displayed in columns 
    ///</summary> 
    public showColumnAttribute() 
    { 
     Format = String.Empty; 
     Title = String.Empty; 
     ReadOnly = false; 
     Width = 0; 
    } 
} 

Et un constructeur:

///<summary> 
    ///Extracts the properties of the supplied type that are to be displayed 
    ///<para>The type must be a class or an InvalidOperationException will be thrown</para> 
    ///</summary> 
    public Virtualiser(Type t) 
    { 
     if (!t.IsClass) 
      throw new InvalidOperationException("Supplied type is not a class"); 

     List<VirtualColumnInfo> definedColumns = new List<VirtualColumnInfo>(); 
     PropertyInfo[] ps = t.GetProperties(); 
     MethodInfo mg, ms; 

     for (int i = 0; i < ps.Length; i++) 
     { 
      Object[] attr = ps[i].GetCustomAttributes(true); 

      if (attr.Length > 0) 
      { 
       foreach (var a in attr) 
       { 
        showColumnAttribute ca = a as showColumnAttribute; 
        if (ca != null) 
        { 
         mg = ps[i].GetGetMethod(); 
         if (mg != null) 
         { 
          ms = ps[i].GetSetMethod(); 
          definedColumns.Add 
          (
           new VirtualColumnInfo 
           (
            ps[i].Name, ca.Width, ca.ReadOnly, ca.Title == String.Empty ? ps[i].Name : ca.Title, 
            ca.Format, mg, ms 
           ) 
          ); 
         } 
         break; 
        } 
       } 
      } 
     } 
     if (definedColumns.Count > 0) 
      columns = definedColumns.ToArray(); 
    } 

Ceci extrait les propriétés publiques de la classe et des fournitures a marqué des éléments à la DataGridView sous forme de colonnes avec un en-tête, format, etc. L'effet de tout cela (et du reste du code manquant) était que tout type pouvait être virtualisé dans un dataGridView simplement en marquant des propriétés publiques et en appelant le virtualiseur une fois pour un type donné:

#region Virtualisation 
    static readonly Virtualiser Virtual = new Virtualiser(typeof(UserRecord)); 
    [XmlIgnore] // just in case! 
    public static int ColumnCount { get { return Virtual.ColumnCount; } } 
    public static VirtualColumnInfo ColumnInfo(int column) 
    { 
     return Virtual.ColumnInfo(column); 
    } 

    public Object GetItem(int column) 
    { 
     return Virtual.GetItem(column, this); 
    } 
    /* 
    ** The supplied item should be a string - it is up to this method to supply a valid value to the property 
    ** setter (this is the simplest place to determine what this is and how it can be derived from a string). 
    */ 
    public void SetItem(int column, Object item) 
    { 
     String v = item as String; 
     int t = 0; 
     if (v == null) 
      return; 
     switch (Virtual.GetColumnPropertyName(column)) 
     { 
      case "DisplayNumber": 
       if (!int.TryParse(v, out t)) 
        t = 0; 

       item = t; 
       break; 
     } 
     try 
     { 
      Virtual.SetItem(column, this, item); 
     } 
     catch { } 
    } 
    #endregion 

Le nombre de colonnes, leurs propriétés et l'ordre peuvent être spécifiées automatiquement en créant un certain nombre de propriétés publiques dérivées à partir des données de classe:

 #region Display columns 
    [showColumn(ReadOnly = true, Width = 100, Title = "Identification")] 
    public String DisplayIdent 
    { 
     get 
     { 
      return ident; 
     } 
     set 
     { 
      ident = value; 
     } 

    } 
    [showColumn(Width = 70, Title = "Number on Roll")] 
    public int DisplayNumber 
    { 
     get 
     { 
      return number; 
     } 
     set 
     { 
      number = value; 
     } 
    } 
    [showColumn(Width = -100, Title = "Name")] 
    public string DisplayName 
    { 
     get 
     { 
      return name == String.Empty ? "??" : name; 
     } 
     set 
     { 
      name = value; 
     } 
    } 
    #endregion 

Ceci virtualiser une classe pour datagridview pour afficher et modifier données et je l'ai utilisé plusieurs fois au cours des années et l'extraction des propriétés à afficher est exactement ce qui est nécessaire pour la sérialisation XML, en effet, il a beaucoup de mêmes caractéristiques.J'allais adapter cette méthode pour faire le même travail pour la sérialisation XML mais quelqu'un l'a déjà fait au https://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=474453, j'espère que vous pourrez utiliser cette méthode pour résoudre votre problème.

0

Cela fonctionne pour moi:

public Form1() 
    { 
     InitializeComponent(); 
    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 
     Entity entity = new Entity(); 
     entity.CausedBy = new List<Entity>(); 
     entity.CausedBy.Add(new Subclass1()); 
     entity.CausedBy.Add(new Subclass2()); 
     entity.CausedBy.Add(new Subclass2()); 
     entity.CausedBy.Add(new Subclass1()); 
     entity.CausedBy.Add(new Subclass1()); 
     entity.Save(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Test.txt")); 
    } 
} 
[Serializable] 
[XmlRoot("Entity")] 
public class Entity 
{ 
    [XmlArray("CausedBy")] 
    [XmlArrayItem("SubClass1", typeof(Subclass1))] 
    [XmlArrayItem("SubClass2", typeof(Subclass2))] 
    public List<Entity> CausedBy { get; set; } 

} 

[Serializable] 
[XmlRoot("Subclass1")] 
public class Subclass1 : Entity 
{ 
    [XmlIgnore] 
    String t = DateTime.Now.ToShortDateString(); 

    public String SubClass1Item { get { return "Test1 " + t; } set { } } 
} 

[Serializable] 
[XmlRoot("Subclass2")] 
public class Subclass2 : Entity 
{ 
    [XmlIgnore] 
    String t = DateTime.Now.ToString(); 

    public String SubClass2Item { get { return "Test2 " + t; } set { } } 
} 

Il produit:

<Entity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <CausedBy> 
    <SubClass1> 
     <SubClass1Item>Test1 20/09/2017</SubClass1Item> 
    </SubClass1> 
    <SubClass2> 
     <SubClass2Item>Test2 20/09/2017 01:06:55</SubClass2Item> 
    </SubClass2> 
    <SubClass2> 
     <SubClass2Item>Test2 20/09/2017 01:06:55</SubClass2Item> 
    </SubClass2> 
    <SubClass1> 
     <SubClass1Item>Test1 20/09/2017</SubClass1Item> 
    </SubClass1> 
    <SubClass1> 
     <SubClass1Item>Test1 20/09/2017</SubClass1Item> 
    </SubClass1> 
    </CausedBy> 
</Entity> 
+0

1, la sous-classe2 s'étend de la sous-classe 1 2, oui je sais que je peux définir l'élément de tableau en définissant le type dans la classe entité (j'avais mis cela dans la question) la chose est que j'aurai beaucoup de sous classes et ne veulent pas avoir à tout définir dans la classe 'entité'. Je veux le faire automatiquement en fonction d'un attribut ou le type ou quelque chose –

+0

Désolé, typo.The sortie est la même, sauf pour le contenu de l'enregistrement: 20/09/2017 Test1< SubClass2Item> Test2 20/09/2017 01:25:34

+0

Vous définissez toujours l'élément de tableau et le nom de type dans la classe d'entité. C'est ce que je ne veux pas. J'ai déjà commenté dans l'original. Je veux un moyen de définir tel dans la sous-classe eux-mêmes –