简体   繁体   English

Xml列表序列化和节点类型名称

[英]Xml List Serialization and Node Type Names

Ive come across multiple questions and answers on here but none specific to my situation. 我在这里遇到了多个问题和答案,但都没有针对我的情况。

I have a class 'Entity' with multiple classes that extend off of it. 我有一个带有多个扩展类的“​​实体”类。 I want the serialization to hit the list and understand and use the type of each item for the node name. 我希望序列化能够打入列表,并理解并使用每个项目的类型作为节点名称。

Now, I can use what is commented out (define each array item in the main class and define the name of such by using [XmlArrayItem("Subclass1", typeof(subclass1)] but I want to keep all definitions in their subclass and I will be having too many subclasses to define everything in the main entity class...Is there anyway to achieve this? 现在,我可以使用注释掉的内容(在主类中定义每个数组项,并通过使用[XmlArrayItem(“ Subclass1”,typeof(subclass1)]]定义其名称,但我想将所有定义保留在其子类中,将有太多的子类来定义主实体类中的所有内容...总有办法实现吗?

I have tried using [XmlType(TypeName="...")] for the subclasses and so on but that did not work. 我尝试对子类使用[XmlType(TypeName =“ ...”)],依此类推,但没有用。

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

Serializing the above code after creating an entity and adding a Subclass1 and Subclass2 to the list 'CausedBy' class results in the following: 创建实体并将Subclass1和Subclass2添加到列表“ CausedBy”类后,序列化上述代码将导致以下结果:

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

I would like the output to show: 我希望输出显示:

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

Since I totally failed to read the question to begin with, here's a new answer (it's a bit of a tl;dr, so you can always skip to the end and follow the link): 由于我完全没有读懂开头的问题,因此这里是一个新答案(有点像tl; dr,因此您始终可以跳到结尾并点击链接):

It isn't possible to get the built in serializer class to work because you don't wish to add the attributes that it needs to be able to operate. 无法使内置的序列化程序类起作用,因为您不希望添加它需要能够运行的属性。 Your only option is to seralize the class yourself, however, this need not be as tedious as it sounds; 您唯一的选择是自己上课,但是这不需要听起来那么乏味。 I had a similar issue a few years ago with DataGridView in virtual mode and produced a generic virtualizer that could be used to virtualize the data for display; 几年前,我在虚拟模式下使用DataGridView遇到了类似的问题,并制作了一个通用虚拟化程序,可用于虚拟化要显示的数据。 it used a custom attribute: 它使用了一个自定义属性:

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

And a constructor: 还有一个构造函数:

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

This extracts the public properties of the class and supplies marked items to the DataGridView as columns together with a header, format, etc. 这将提取类的公共属性,并将带有标记的项目作为列以及标题,格式等提供给DataGridView。

The effect of all of this (and the rest of the missing code) was that any type could be virtualized in a dataGridView simply by tagging public properties and calling the virtualizer once for a given type: 所有这些(以及其余缺少的代码)的结果是,只需标记公共属性并为给定类型调用一次虚拟器,就可以在dataGridView中将任何类型虚拟化:

    #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

The number of columns, their properties and order can be specified automatically by creating a number of public properties derived from the class data: 可以通过创建一些从类数据派生的公共属性来自动指定列数,它们的属性和顺序:

        #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

This would virtualize any class for dataGridView to display and edit data and I used it many times over the years and the extraction of properties to display is exactly what is required for XML serialization, indeed, it has a lot of the same characteristics. 这将虚拟化dataGridView的任何类以显示和编辑数据,并且我多年来使用了很多次,提取属性以显示的正是XML序列化所必需的,实际上,它具有许多相同的特性。

I was going to adapt this method to do the same job for XML serialization but someone has already done it at https://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=474453 , I hope you can make use of this method to solve your problem. 我打算改用这种方法来完成XML序列化的相同工作,但是有人已经在https://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=474453上做到了,希望您可以使用解决您的问题的方法。

This works for me: 这对我有用:

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

It produces: 它产生:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM