Pregunta

I need to deserialize the following 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:

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? I can always build my own parser, but I would much prefer to use a standard approach, such as System.Xml.Serialization.XmlSerializer.

¿Fue útil?

Solución 2

The format of this is quite crazy. Unfortunately this means you will need to parse the xml manually with XDocument or XmlDocument. 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; }
    }
}

Otros consejos

You could use 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.

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. This example here works only, id you know the exact amount of those tags.

There is no standard way to handle elements with different names. Because your xml is not standard 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:

<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, e.g. by filtering out <NUMBER> element and just enumerating all other elements

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.

The way that I used for solving this problem of mine was saving them to .xml files and retrieved from the .xml file. 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:

SaveTimeWindow(TimeWindow, yourPath);

and Load it based on:

TimeWindow[] t = LoadTime(yourPath);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top