简体   繁体   中英

How to create sets of the serialized objects C#

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

[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()
    {

    }
}

Ie root can include different objects, and they have to be serialized...

I have a xml-format approximately of such look:

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

In which some elements can change a little (Document, Document with tags, Document with the status, etc.), included in others (for example, report incl. in scheme) ... and do not know how to change in the future.

I want to construct a set of "formats" which will also have various components, to be substituted... Maybe for this purpose you shouldn't use serialization, and to define set of attributes, and a reflection to process objects and to form xml (approximately just as XmlSerializer)???

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<object> _test; [XmlElement("Report", Type=typeof(Report))] [XmlElement("DifferentReport", Type = typeof(DifferentReport))] public List<object> 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<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; } } 

    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<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. 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<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); } } 

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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