【问题标题】:How to deserialize a numbered array of XML elements如何反序列化 XML 元素的编号数组
【发布时间】:2013-12-19 08:52:19
【问题描述】:

我需要反序列化以下 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>

我需要的输出是TimeWindow 对象的集合(列表、数组等),例如:

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

是否有处理NO0NO1NO2、...元素的标准方法? 我总是可以构建自己的解析器,但我更喜欢使用标准方法,例如System.Xml.Serialization.XmlSerializer

【问题讨论】:

  • 每个元素的名称指定它的类型(其内容的格式)。您对同一类型使用不同的名称。你能有更多或更少的 4 个&lt;NOx&gt; 项目吗?
  • 问题正是元素的名称不一致。我无法控制 XML,因为我从第三方服务接收它。理论上可以有无限数量的元素 - 尽管通常会有 5-20 个元素。
  • &lt;NUMBER&gt;10&lt;/NUMBER&gt; 是否指定了&lt;NOx&gt; 节点的数量?
  • 附带说明,当问题明确指出@ThomasJørgensen 能够编写自定义解析器但想知道时,我真的不明白为什么所有答案都集中在 解析如果已经存在针对这种特殊情况的“预建”机制。
  • @AndreiV 有答案明确指出没有可用的机制,因为格式是循环的。

标签: c# xml xml-parsing xml-deserialization


【解决方案1】:

您可以使用 LINQ to XML。比如……

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

您可能希望在 FROM 和 TO 元素周围添加一些空值检查,以确保它们存在于数据中。

【讨论】:

  • 旁注 - 您可以使用 s.Name.LocalName 并且最好将元素转换为字符串而不是访问其 Value 属性
  • 只是想知道为什么使用强制转换比使用 Value 属性更好?
  • @JevgenijNekrasov 如果找不到元素(或属性),您将使用null 字符串而不是NullReferenceException
  • 如果没有找到该元素仍然是一个错误。你对Parse 的调用将会爆炸。如果此文件有效,则该元素将存在,因为 NUMBER 标记将确保它。
【解决方案2】:

这个格式很疯狂。不幸的是,这意味着您需要使用XDocumentXmlDocument 手动解析xml。让我们使用前者,因为它更容易:

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

【讨论】:

    【解决方案3】:

    可能的做法是:

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

    如果你有这个类和帮助类,你可以简单地执行以下操作(当然是在另一个类中):

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

    在此之后,您可以通过

    访问(目前是“字符串”格式的)数据
    timeWindow.NO0.FROM;
    

    对我来说,这在几天前就奏效了。 但我只是从脑海中写出来的。

    //编辑

    抱歉,我没有意识到“NOx”标签的数量不同。 如果您知道这些标签的确切数量,此示例仅适用于此。

    【讨论】:

    • 这不会让您拥有可变数量的NOx 元素。
    • 杰普,我才意识到,我的错。编辑了答案。谢谢
    【解决方案4】:

    没有标准的方法来处理具有不同名称的元素。因为你的 xml 不是标准的 xml。相同类型的所有子级应具有相同的名称,否则它们将被视为不同的元素。附加信息(如窗口索引)应通过子元素的属性或元素提供,而不是通过元素名称:

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

    因此,您应该手动处理此问题,例如通过过滤掉 &lt;NUMBER&gt; 元素并仅枚举所有其他元素

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

    还可以考虑在 TimeWindow 类中使用 DateTime 属性,因为它们包含日期。

    【讨论】:

      【解决方案5】:

      我用来解决我的这个问题的方法是将它们保存到 .xml 文件并从 .xml 文件中检索。检查以下代码:

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

      对于加载,您可以使用以下内容:

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

      然后您可以根据以下内容保存您的 XML:

      SaveTimeWindow(TimeWindow, yourPath);
      

      并根据以下内容加载它:

      TimeWindow[] t = LoadTime(yourPath);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-06-24
        • 2015-04-25
        • 1970-01-01
        • 1970-01-01
        • 2023-03-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多