简体   繁体   English

如何反序列化XML元素的编号数组

[英]How to deserialize a numbered array of XML elements

I need to deserialize the following XML: 我需要反序列化以下XML:

<TIMEWINDOWS>
    <NUMBER>10</NUMBER>
    <NO0>
        <FROM>22-11-2013 08:00:00</FROM>
        <TO>22-11-2013 11:59:00</TO>
    </NO0>
    <NO1>
        <FROM>22-11-2013 12:00:00</FROM>
        <TO>22-11-2013 15:59:00</TO>
    </NO1>
    <NO2>
        <FROM>23-11-2013 08:00:00</FROM>
        <TO>23-11-2013 11:59:00</TO>
    </NO2>
    <NO3>
        <FROM>23-11-2013 12:00:00</FROM>
        <TO>23-11-2013 15:59:00</TO>
    </NO3>
    ...
</TIMEWINDOWS>

The output that I require is a collection (list, array, whatever) of TimeWindow objects, for example: 我需要的输出是TimeWindow对象的集合(列表,数组等),例如:

public class TimeWindow
{
    public string From { get; set; }
    public string To { get; set; }
}

Is there a standard way to handle the NO0 , NO1 , NO2 , ... elements? 是否有处理NO0NO1NO2 ,...元素的标准方法? I can always build my own parser, but I would much prefer to use a standard approach, such as System.Xml.Serialization.XmlSerializer . 我总是可以构建自己的解析器,但是我更喜欢使用标准方法,例如System.Xml.Serialization.XmlSerializer

You could use LINQ to XML. 您可以使用LINQ to XML。 Something like... 就像是...

        XDocument doc = XDocument.Load("XMLFile1.xml");
        var result = new List<TimeWindow>();
        foreach (XElement s in doc.Elements().Descendants())
        {
            if (s.Name.ToString().StartsWith("NO"))
            {
                var tw = new TimeWindow {From = (string)s.Element("FROM"), 
                    To = (string)s.Element("TO")};
                result.Add(tw);
            }
        }

You'd probably want to add some checking for nulls around the FROM and TO elements to ensure they are present in the data. 您可能希望在FROM和TO元素周围添加一些对null的检查,以确保它们出现在数据中。

A possibility is to do the following: 一种可能是执行以下操作:

public class TimeWindow
{ 
public int number{get;set;}
public Times NO0 = new Times();
public Times NO1 = new Times();
public Times NO2 = new Times();
public Times NO3 = new Times();
}

public class Times()
{
public string FROM{get;set;}
public string TO{get;set;}
}

And if you have this class and help class, you can simply do the following (in another class of course): 如果您有此类和帮助类,则可以简单地执行以下操作(当然是在另一类中):

XmlSerializer serializer = new XmlSerializer(typeof(TimeWindow));
TimeWindow timeWindow = (TimeWindow)serializer.Deserialize(new StreamReader(pathToFile));

After this, you can access the (at present time "string"-formatted) data via 之后,您可以通过访问(当前为“字符串”格式的)数据

timeWindow.NO0.FROM;

For me, this worked some days ago. 对我来说,这在几天前奏效。 But I just wrote it from my mind. 但我只是从脑海中写下来。

//EDIT //编辑

Sorry, I didn't realize that there are different numbers of "NOx"-Tags. 抱歉,我没有意识到有不同数量的“ NOx”标签。 This example here works only, id you know the exact amount of those tags. 此示例仅在此处有效,您知道这些标签的确切数量。

The format of this is quite crazy. 这种格式非常疯狂。 Unfortunately this means you will need to parse the xml manually with XDocument or XmlDocument . 不幸的是,这意味着您将需要使用XDocumentXmlDocument手动解析xml。 Lets use the former as it is easier: 让我们使用前者,因为它更容易:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace Xmlarrayload
{
    class Program
    {
        static void Main(string[] args)
        {
            var document = XDocument.Parse(@"<TIMEWINDOWS>
    <NUMBER>4</NUMBER>
    <NO0>
        <FROM>22-11-2013 08:00:00</FROM>
        <TO>22-11-2013 11:59:00</TO>
    </NO0>
    <NO1>
        <FROM>22-11-2013 12:00:00</FROM>
        <TO>22-11-2013 15:59:00</TO>
    </NO1>
    <NO2>
        <FROM>23-11-2013 08:00:00</FROM>
        <TO>23-11-2013 11:59:00</TO>
    </NO2>
    <NO3>
        <FROM>23-11-2013 12:00:00</FROM>
        <TO>23-11-2013 15:59:00</TO>
    </NO3>
</TIMEWINDOWS>");

            int number = int.Parse(document.Root.Element("NUMBER").Value);
            TimeWindow[] windows = (TimeWindow[])Array.CreateInstance(typeof(TimeWindow), number);

            for (int i = 0; i < number; i++)
            {
                var element = document.Root.Element(string.Format("NO{0}", i));
                TimeWindow window = new TimeWindow
                {
                    //it is extremely important to use the correct culture (invariant) to parse the dates.
                    To = DateTime.ParseExact(element.Element("TO").Value, "dd-MM-yyyy HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat),
                    From = DateTime.ParseExact(element.Element("FROM").Value, "dd-MM-yyyy HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat)
                };
                windows[i] = window;
            }
        }
    }

    public class TimeWindow
    {
        public DateTime From { get; set; }
        public DateTime To { get; set; }
    }
}

There is no standard way to handle elements with different names. 没有标准的方法来处理具有不同名称的元素。 Because your xml is not standard xml. 因为您的xml不是标准xml。 All children of same type should have same names otherwise they considered as different elements. 所有相同类型的子代都应具有相同的名称,否则将它们视为不同的元素。 Additional information (like index of window) should be provided via attributes or elements of child, not via element name: 附加信息(例如window的索引)应通过child的属性或元素提供,而不是通过元素名称提供:

<TimeWindows number="10"> <!-- actually you don't need number attribute here -->
    <TimeWindow index="1">
        <From>22-11-2013 08:00:00</From>
        <To>22-11-2013 11:59:00</To>
    </TimeWindow>
    <TimeWindow index="2">
        <From>22-11-2013 12:00:00</From>
        <To>22-11-2013 15:59:00</To>
    </TimeWindow>
    <TimeWindow index="3">
        <From>23-11-2013 08:00:00</From>
        <To>23-11-2013 11:59:00</To>
    </TimeWindow>
</TimeWindows>

So, you should handle this manually, eg by filtering out <NUMBER> element and just enumerating all other elements 因此,您应该手动处理此问题,例如,过滤掉<NUMBER>元素并枚举所有其他元素

var xdoc = XDocument.Load(path_to_xml);
var windows = xdoc.Root.Elements().Where(e => e.Name.LocalName != "NUMBER")
                  .Select(n => new TimeWindow {
                      From = (string)n.Element("FROM"),
                      To = (string)n.Element("TO")
                   }).ToList();

Also consider to use DateTime properties in your TimeWindow class, because they hold dates. 还应考虑在TimeWindow类中使用DateTime属性,因为它们保存日期。

The way that I used for solving this problem of mine was saving them to .xml files and retrieved from the .xml file. 我用来解决此问题的方法是将它们保存到.xml文件并从.xml文件检索。 Check the below code: 检查以下代码:

 private void SaveTimeWindow(TimeWindow[] time, string filePath)
    {
        //Open a file stream 
        System.IO.FileStream fs = new System.IO.FileStream(filePath, System.IO.FileMode.Create);
        // Create a xml Serializer object
        System.Xml.Serialization.XmlSerializer xmlSer = new System.Xml.Serialization.XmlSerializer(typeof(TimeWindow[]));
        xmlSer.Serialize(fs, time);
        // Close the file stream
        fs.Close();         
    }

For loading you may use the below: 对于加载,您可以使用以下代码:

  private static TimeWindow[] LoadTime(string filePath)
        {
            //Open the XML file
            if (System.IO.File.Exists(filePath))
            {
                System.IO.FileStream fs = new System.IO.FileStream(filePath, System.IO.FileMode.Open);

                // First create a xml Serializer object
                System.Xml.Serialization.XmlSerializer xmlSer = new System.Xml.Serialization.XmlSerializer(typeof(TimeWindow[]));
                // Deserialize the Matrix object
                TimeWindow[] time= (TimeWindow[])xmlSer.Deserialize(fs);

                // Close the file stream
                fs.Close();
                return time;
            }
            else
            {
                return null;
            }

        }

Then you may save your XML based on: 然后,您可以基于以下内容保存XML:

SaveTimeWindow(TimeWindow, yourPath);

and Load it based on: 并根据以下内容加载:

TimeWindow[] t = LoadTime(yourPath);

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

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