2017-09-19 3 views
0

Ich bin hier auf mehrere Fragen und Antworten gestoßen, aber keine spezifische für meine Situation.Xml List Serialization und Knotentypnamen

Ich habe eine Klasse 'Entity' mit mehreren Klassen, die davon ausgehen. Ich möchte, dass die Serialisierung die Liste trifft und den Typ jedes Elements für den Knotennamen versteht und verwendet.

Nun kann ich was heraus kommentiert (definieren jedes Array-Element in der Hauptklasse und definieren Sie den Namen eines solchen mit [XmlArrayItem („Subclass1“, typeof (subclass1)], aber ich möchte alle Definitionen halten ihre Unterklasse und ich habe zu viele Unterklassen, um alles in der Haupteinheitenklasse zu definieren ... Gibt es trotzdem, um dies zu erreichen?

Ich habe versucht mit [XmlType (TypeName = "...")] für die Unterklassen und so weiter, aber das hat nicht funktioniert.

[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... 
} 

den obigen Code Serialisierung nach einer Einheit zu schaffen und eine Subclass1 und Subclass2 in die Liste ‚Cause Hinzufügen dBy‘Klasse Ergebnisse in der folgenden:

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

ich den Ausgang möchte zeigen:

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

Antwort

1

Da habe ich total versagt das zu lesen Frage zu beginnen, hier ist eine neue Antwort (es ist ein bisschen ein Tl; dr, so können Sie immer bis zum Ende überspringen und folgen Sie dem Link):

Es ist nicht möglich, die eingebaute Serializer-Klasse zu bekommen arbeiten, weil Sie nicht die Attribute hinzufügen möchten, die es benötigt, um zu funktionieren. Ihre einzige Option besteht darin, die Klasse selbst zu seralisieren, dies muss jedoch nicht so langwierig sein, wie es sich anhört; Ich hatte vor ein paar Jahren ein ähnliches Problem mit DataGridView im virtuellen Modus und produzierte einen generischen Virtualisierer, der verwendet werden könnte, um die Daten für die Anzeige zu virtualisieren; es verwendet, um ein benutzerdefiniertes Attribut:

[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; 
    } 
} 

Und einen Konstruktor:

///<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(); 
    } 

Diese extrahiert die öffentlichen Eigenschaften der Klasse und liefert markierte Elemente in die Datagridview als Spalten zusammen mit einem Header, Format, usw. Der Effekt von all dem (und dem Rest des fehlenden Codes) war, dass jeder Typ in einem dataGridView virtualisiert werden konnte, indem einfach öffentliche Eigenschaften mit Tags versehen wurden und der Virtualisierer für einen gegebenen Typ einmal aufgerufen wurde:

#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 

die Anzahl der Spalten, deren Eigenschaften und Reihenfolge kann von der Klassendaten abgeleitet, indem eine Anzahl von öffentlichen Eigenschaften automatisch angegeben werden:

 #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 

Dies würde jede Klasse für Datagridview virtualisieren anzuzeigen und zu bearbeiten Daten und ich habe es im Laufe der Jahre oft verwendet und die Extraktion von Eigenschaften zur Anzeige ist genau das, was für XML-Serialisierung erforderlich ist, tatsächlich hat es viele der gleichen Eigenschaften.

Ich wollte diese Methode anpassen, um den gleichen Job für XML-Serialisierung zu tun, aber jemand hat es bereits bei https://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=474453 getan, ich hoffe, Sie können diese Methode verwenden, um Ihr Problem zu lösen.

0

Dies funktioniert für mich:

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 { } } 
} 

Es produziert:

<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, die Unterklasse2 erstreckt sich von Unterklasse1 2, ja ich weiß, ich kann das Array-Element definieren, indem Sie den Typ in der Entitätsklasse (ich hatte das in der Frage) die Sache ist, ich werde viele Unterklassen haben und möchte nicht alle in der Klasse 'entity' definieren müssen. Ich möchte es automatisch basierend auf einem Attribut oder Typ oder etwas tun –

+0

Sorry, typo.The Ausgabe ist die gleiche, mit Ausnahme des Inhalts des Datensatzes: Test1 20/09/2017 < SubClass2Item> Test2 20/09/2017 01:25:34

+0

Sie definieren immer noch den Array-Element- und Typnamen in der Entitätsklasse. Das will ich nicht. Ich habe das schon im Original auskommentiert. Ich möchte einen Weg, um solche in der Unterklasse selbst zu definieren –

Verwandte Themen