How to create sets of the serialized objects C#

后端 未结 1 580
庸人自扰
庸人自扰 2021-01-28 15:46

There are various types, in a special case which can be configured in different ways. How to serialize them?

[Serializable]
[XmlRoot(\"RootXml\", Namespace = \"\         


        
1条回答
  •  执笔经年
    2021-01-28 16:16

    You are trying to serialize and deserialize data with polymorphic fields. You have a few options here:

    1. If you know in advance all possible types that might be encountered in a polymorphic field, you can use attributes to tell XmlSerializer how to serialize and deserialize each type. In particular, for a polymorphic field, apply [XmlElement("DerivedElementName", Type = typeof(DerivedElementType))] for every derived type that might be encountered.

      For instance, simplifying your RootXml class somewhat, the following allows for two different types of report to be serialized:

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

      And then later, both of the following can be serialized and deserialized:

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

      Note that you can use the same technique with polymorphic lists, in which case the serializer will expect sequences of elements with the specified names:

      [XmlRoot("RootXml", Namespace = "")]
      public class RootXml
      {
          public RootXml() { }
      
          List _test;
      
          [XmlElement("Report", Type=typeof(Report))]
          [XmlElement("DifferentReport", Type = typeof(DifferentReport))]
          public List Data
          {
              get { return _test; }
              set { _test = value; }
          }
      }
      
      
    2. If the XML could be anything and you don't know what it might contain (because you must deserialize XML from the future versions and reserialize it without data loss, for example) you may need to load your XML into an XDocument then manually search for data using Linq-to-XML. For information on how to do this, see here: Basic Queries (LINQ to XML).

    3. You could adopt a hybrid approach where you load the XML into an XDocument, then deserialize and serialize familiar portions with XmlSerializer, using the following extension methods:

      public static class XObjectExtensions
      {
          public static T Deserialize(this XContainer element)
          {
              return element.Deserialize(new XmlSerializer(typeof(T)));
          }
      
          public static T Deserialize(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(this T obj, bool omitStandardNamespaces = true)
          {
              return obj.Serialize(new XmlSerializer(obj.GetType()), omitStandardNamespaces);
          }
      
          public static XElement Serialize(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;
          }
      }
      

      Then use them to pick out and deserialize known portions of your XML as follows:

          var doc = XDocument.Parse(xml);
          var reportElement = doc.Root.Element("Report");
          if (reportElement != null)
          {
              var report1 = doc.Root.Element("Report").Deserialize();
              // 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. OK, given that you are using c# 2.0, you can load your Xml into an XmlDocument and use it as described here: Process XML Data Using the DOM Model. This is a precursor API to Linq-to-XML and is somewhat harder to work with -- but nevertheless totally functional.

      You can also adopt the hybrid approach and use XmlSerializer to deserialize and re-serialize known chunks of an XmlDocument. Here are some extension methods for this purpose -- but since you're using c# 2.0, you must remove the this keyword:

      public static class XmlNodeExtensions
      {
          public static XmlElement SerializeToXmlElement(this T o, XmlElement parent)
          {
              return SerializeToXmlElement(o, parent, new XmlSerializer(o.GetType()));
          }
      
          public static XmlElement SerializeToXmlElement(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(this T o)
          {
              return SerializeToXmlDocument(o, new XmlSerializer(o.GetType()));
          }
      
          public static XmlDocument SerializeToXmlDocument(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(this XmlElement element)
          {
              return Deserialize(element, new XmlSerializer(typeof(T)));
          }
      
          public static T Deserialize(this XmlElement element, XmlSerializer serializer)
          {
              using (var reader = new XmlNodeReader(element))
                  return (T)serializer.Deserialize(reader);
          }
      }
      

      Given those methods, you can do things like:

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

      As you can see this is quite similar to what is possible with Linq-to-XML.

    5. 0 讨论(0)
      提交回复
      热议问题