如何创建序列化对象的集合C#

时间:2015-03-27 09:24:16

标签: c# xml serialization c#-2.0

有各种类型,在特殊情况下可以以不同方式配置。如何序列化它们?

[Serializable]
[XmlRoot("RootXml", Namespace = "")]
public class RootXml
{
    object _schemaVersion;

    [XmlElement("SchemaVersion")]
    public object SchemaVersion
    {
        get { return _schemaVersion; }
        set { _schemaVersion = value; }
    }

    List<object> _test;

    [XmlElement("Test")]
    public List<object> Test
    {
        get { return _test; }
        set { _test = value; }
    }

    public RootXml()
    {

    }
}

即。 root可以包含不同的对象,它们必须被序列化......

我的xml格式大约是这样的 看:

<?xml version="1.0" encoding="windows-1251"?>
<RootXml>
  <SchemaVersion Number="" />
  <Report Code="">
    <Period Code="" Date="">
      <Source ClassCode="" Code="">
        <Form Code="">
          <Column Num="1" Name="" />
          <Column Num="2" Name="" />
          <Column Num="3" Name="" />         
          <Document>
            <Data code="11" />          
            <Data code="12">
              <Px Num="1" Value="1" />
              <Px Num="2" Value="1" />
              <Px Num="4" Value="2" />
              <Px Num="5" Value="2" />
            </Data>
            <Data code="13" />
          </Document>
        </Form>
      </Source>
    </Period>
  </Report>
</RootXml>

其中一些元素可以稍微改变(文档,带有标签的文档,带有状态的文档等), 包含在其他人中(例如,报告中包括方案)......并且不知道将来如何改变。

我想构建一组&#34;格式&#34;还将有各种组件,可以替代...... 也许为了这个目的,你不应该使用序列化,并定义 一组属性,一个反射来处理对象和形成xml(大约就像XmlSerializer一样)???

1 个答案:

答案 0 :(得分:0)

您正在尝试使用多态字段序列化和反序列化数据。你有几个选择:

  1. 如果您事先知道多态字段中可能遇到的所有可能类型,则可以使用attributes告诉XmlSerializer如何序列化和反序列化每种类型。特别是,对于多态字段,请对可能遇到的每种派生类型应用[XmlElement("DerivedElementName", Type = typeof(DerivedElementType))]

    例如,稍微简化RootXml类,以下内容允许序列化两种不同类型的报告:

    [XmlRoot("Report", Namespace = "")]
    public class Report
    {
        [XmlAttribute]
        public string Code { get; set; }
    
        [XmlElement]
        public decimal TotalCost { get; set; }
    }
    
    [XmlRoot("DifferentReport", Namespace = "fuuuu")]
    public class DifferentReport
    {
        public DifferentReport() { }
    
        public DifferentReport(string code, string value)
        {
            this.Code = code;
            this.Value = value;
        }
    
        [XmlAttribute]
        public string Code { get; set; }
    
        [XmlText]
        public string Value { get; set; }
    }
    
    [XmlRoot("RootXml", Namespace = "")]
    public class RootXml
    {
        public RootXml() { }
    
        object _test;
    
        [XmlElement("Report", Type=typeof(Report))]
        [XmlElement("DifferentReport", Type = typeof(DifferentReport))]
        public object Data
        {
            get { return _test; }
            set { _test = value; }
        }
    }
    

    然后,以下两个都可以序列化和反序列化:

        var root1 = new RootXml { Data = new Report { Code = "a code", TotalCost = (decimal)101.01 } };
        var root2 = new RootXml { Data = new DifferentReport { Code = "a different code", Value = "This is the value of the report" } };
    

    请注意,您可以对多态列表使用相同的技术,在这种情况下,序列化程序将期望具有指定名称的元素序列:

    [XmlRoot("RootXml", Namespace = "")]
    public class RootXml
    {
        public RootXml() { }
    
        List<object> _test;
    
        [XmlElement("Report", Type=typeof(Report))]
        [XmlElement("DifferentReport", Type = typeof(DifferentReport))]
        public List<object> Data
        {
            get { return _test; }
            set { _test = value; }
        }
    }
    
  2. 如果XML可能是任何内容而您不知道它可能包含什么(因为您必须从未来版本反序列化XML并重新序列化它而不会丢失数据),您可能需要将XML加载到然后XDocument使用Linq-to-XML手动搜索数据。有关如何执行此操作的信息,请参阅此处:Basic Queries (LINQ to XML)

  3. 您可以采用混合方法,将XML加载到XDocument,然后使用以下扩展方法对XmlSerializer的熟悉部分进行反序列化和序列化:

    public static class XObjectExtensions
    {
        public static T Deserialize<T>(this XContainer element)
        {
            return element.Deserialize<T>(new XmlSerializer(typeof(T)));
        }
    
        public static T Deserialize<T>(this XContainer element, XmlSerializer serializer)
        {
            using (var reader = element.CreateReader())
            {
                object result = serializer.Deserialize(reader);
                if (result is T)
                    return (T)result;
            }
            return default(T);
        }
    
        public static XElement Serialize<T>(this T obj, bool omitStandardNamespaces = true)
        {
            return obj.Serialize(new XmlSerializer(obj.GetType()), omitStandardNamespaces);
        }
    
        public static XElement Serialize<T>(this T obj, XmlSerializer serializer, bool omitStandardNamespaces = true)
        {
            var doc = new XDocument();
            using (var writer = doc.CreateWriter())
            {
                XmlSerializerNamespaces ns = null;
                if (omitStandardNamespaces)
                    (ns = new XmlSerializerNamespaces()).Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
                serializer.Serialize(writer, obj, ns);
            }
            return doc.Root;
        }
    }
    

    然后使用它们来挑选和反序列化XML的已知部分,如下所示:

        var doc = XDocument.Parse(xml);
        var reportElement = doc.Root.Element("Report");
        if (reportElement != null)
        {
            var report1 = doc.Root.Element("Report").Deserialize<Report>();
            // Do something with the report.
    
            // Create a different report 
            var differentReport = new DifferentReport { Code = report1.Code + " some more code", Value = "This is the value of the report" };
            var differentElement = differentReport.Serialize();
    
            reportElement.AddAfterSelf(differentElement);
            reportElement.Remove();
        }
    
  4. 好的,鉴于您使用的是c#2.0,您可以将Xml加载到XmlDocument并按照此处所述使用它:Process XML Data Using the DOM Model。这是Linq-to-XML的前身API,有点难以使用 - 但仍然完全正常。

    您还可以采用混合方法并使用XmlSerializer反序列化并重新序列化XmlDocument的已知块。以下是一些extension methods用于此目的 - 但由于您使用的是c#2.0,因此必须删除this关键字

    public static class XmlNodeExtensions
    {
        public static XmlElement SerializeToXmlElement<T>(this T o, XmlElement parent)
        {
            return SerializeToXmlElement(o, parent, new XmlSerializer(o.GetType()));
        }
    
        public static XmlElement SerializeToXmlElement<T>(this T o, XmlElement parent, XmlSerializer serializer)
        {
            int oldCount = parent.ChildNodes.Count;
            XPathNavigator navigator = parent.CreateNavigator();
            using (XmlWriter writer = navigator.AppendChild())
            {
                writer.WriteComment(""); // Kludge suggested here: https://social.msdn.microsoft.com/Forums/en-US/9ff20a3c-913d-4c6f-a18a-c10040290862/how-to-xmlserialize-directly-into-xmldocument?forum=asmxandxml
                serializer.Serialize(writer, o);
            }
            XmlElement returnedElement = null;
            for (int i = parent.ChildNodes.Count - 1; i >= oldCount; i--)
            {
                XmlComment comment = parent.ChildNodes[i] as XmlComment;
                if (comment != null)
                {
                    parent.RemoveChild(comment);
                }
                else
                {
                    returnedElement = (parent.ChildNodes[i] as XmlElement) ?? returnedElement;
                }
            }
            return returnedElement;
        }
    
        public static XmlDocument SerializeToXmlDocument<T>(this T o)
        {
            return SerializeToXmlDocument(o, new XmlSerializer(o.GetType()));
        }
    
        public static XmlDocument SerializeToXmlDocument<T>(this T o, XmlSerializer serializer)
        {
            XmlDocument doc = new XmlDocument();
            using (XmlWriter writer = doc.CreateNavigator().AppendChild())
                serializer.Serialize(writer, o);
            return doc;
        }
    
        public static T Deserialize<T>(this XmlElement element)
        {
            return Deserialize<T>(element, new XmlSerializer(typeof(T)));
        }
    
        public static T Deserialize<T>(this XmlElement element, XmlSerializer serializer)
        {
            using (var reader = new XmlNodeReader(element))
                return (T)serializer.Deserialize(reader);
        }
    }
    

    鉴于这些方法,您可以执行以下操作:

        // Load the document from XML
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(xml);
    
        // Find all nodes with name "Report"
        foreach (XmlElement reportNode in doc.SelectNodes("/RootXml/Report"))
        {
            // Deserialize as a Report
            Report report = XmlNodeExtensions.Deserialize<Report>(reportNode);
            // Do something with it 
    
            // Create a new Report, based on the original report.
            DifferentReport differentReport = new DifferentReport(report.Code + " some more code", "This is the value of the report"); ;
            // Add the new report to the children of RootXml
            XmlElement newNode = XmlNodeExtensions.SerializeToXmlElement(differentReport, (XmlElement)reportNode.ParentNode);
        }
    

    正如您所看到的,这与Linq-to-XML非常相似。