【问题标题】:C# Parsing XML with XmlReader and XmlWriterC# 使用 XmlReader 和 XmlWriter 解析 XML
【发布时间】:2020-11-17 21:54:56
【问题描述】:

我正在解析这个 XML:

<?xml version="1.0" encoding="ISO-8859-2"?>
<tabela_kursow typ="A" uid="20a219">
   <numer_tabeli>219/A/NBP/2020</numer_tabeli>
   <data_publikacji>2020-11-09</data_publikacji> 
       <pozycja>
          <nazwa_waluty>bat (Tajlandia)</nazwa_waluty>
          <przelicznik>1</przelicznik>
          <kod_waluty>THB</kod_waluty>
          <kurs_sredni>0,1236</kurs_sredni>
       </pozycja>
       <pozycja>
         <nazwa_waluty>dolar amerykanski</nazwa_waluty>
         <przelicznik>1</przelicznik>
         <kod_waluty>USD</kod_waluty>
         <kurs_sredni>3,7787</kurs_sredni>
       </pozycja>
</tabela_kursow>

我正在使用 XmlReader 从 URL 读取它并使用 XmlWriter 将其写入我的 XML 文件。但我也有允许的货币列表,所以我不想将所有货币从 URL XML 写入我的 XML 文件。 但是,当我开始编写 section 时,如何有效地测试货币是否在我的列表中,但只有当我到达 标签时,我才能测试我是否想要这种货币。我可以将之前的行写到某个地方来缓冲,然后再决定是否需要它们吗?

这是我当前的代码,它在没有任何条件的情况下读取所有货币:

    public void ApiCall()
    {
        bool today = false;
        Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
        Tools.Log("Start processing file: " + cf.rateUrl);
        XmlWriter writer = XmlWriter.Create(cf.outXml);
        writer.WriteStartDocument();

        //*** FOR TESTS ***
        using (XmlReader reader = XmlReader.Create(new StreamReader("LastA.xml", Encoding.GetEncoding(28592))))
        //*** FOR TESTS ***            
        //using (XmlReader reader = XmlReader.Create(new StreamReader(WebRequest.Create(cf.rateUrl).GetResponse().GetResponseStream(), Encoding.GetEncoding(28592))))
        {
            while (reader.Read())
            {
                switch (reader.NodeType)
                {
                    case XmlNodeType.Element:
                        if (reader.Name.Equals("data_publikacji"))
                        {
                            today = true;
                        }
                        writer.WriteStartElement(reader.Name);
                        while (reader.MoveToNextAttribute())
                        {
                            writer.WriteAttributeString(reader.Name, reader.Value);
                        }
                        break;
                    case XmlNodeType.Text:
                        writer.WriteNode(reader, true);
                        writer.WriteEndElement();
                        if (today)
                        {
                            writer.WriteStartElement("our_date");
                            var settings = new XmlReaderSettings();
                            settings.ConformanceLevel = ConformanceLevel.Fragment;
                            StringReader scontent = new StringReader(string.Format(cf.today.Year + "-" + cf.today.Month + "-" + cf.today.Day));
                            XmlReader ourDate = XmlReader.Create(scontent, settings);
                            writer.WriteNode(ourDate, true);
                            writer.WriteEndElement();
                            today = false;
                        }
                        break;
                    case XmlNodeType.EndElement:
                        writer.WriteNode(reader, false);
                        break;
                }
            }
        }
        writer.WriteEndDocument();
        writer.Close();
    }

【问题讨论】:

  • xml文件中没有根元素吗?
  • 对不起,是的,我更新了原始帖子,带有完整的 XML 示例。
  • @PavelMatras,使用 XSLT 更容易实现您的需求。
  • 在大型 xml 大小(例如数百兆字节)的情况下,使用流式 XmlReader/XmlWriter 是合理的。 |在你的情况下,我会使用 linq to xml: XDocument/XElement.

标签: c# xml


【解决方案1】:

您可以使用 XPath 表达式,仅选择“kod_waluty”与允许货币列表匹配的节点,简而言之:

xmlDoc.SelectNodes("//pozycja[contains('THB', kod_waluty)]"))

如果您使用这个小提琴,我相信您将能够适应您的需要! https://dotnetfiddle.net/jv98UZ(链接更新于 11 月 17 日)

NB 该示例仅匹配一种货币,但您可以在这样的列表中输入更多代码(XXX YYY ZZZ):

xmlDoc.SelectNodes("//pozycja[contains('THB XXX YYY ZZZ', kod_waluty)]"))

编辑:如果您真的喜欢示例中的流程并希望尽可能少地进行更改,那么您可以写入临时对象(也许是 XmlNode?),而不是直接写入您的 XmlWriter“作家”,然后一次您已经验证了货币,您可以将临时对象写入“writer”,或者在它无效的情况下忽略它。

【讨论】:

  • 所以解决方案是重写我的代码以使用 XmlDocument。只有 XmlReader 和 XmlWriter 我无法做到这一点?
  • 我想你想要 XmlReader.ReadSubtree。这会给你另一个读者,你一个然后测试或货币,如果匹配,把它写出来。很久没玩这个了。
  • 我尝试使用 XmlReader.ReadSubtree,它看起来不错,但是当我尝试处理由 ReadSubtree 创建的这个新实例(例如 Read())时,XmlReader 的原始实例也会受到影响;-(跨度>
  • @PavelMatras - 没错。根据docsXmlReader 表示提供对 XML 数据的快速、非缓存、只进的访问的阅读器。因此它没有任何可以回顾以前的值,或者窥视即将到来的价值观。如果您的文件很大,您可以使用XElement.ReadFrom 分块读取它,如How to read large xml file without loading it in memory and using XElement 所示。
  • @PavelMatras 如果您想稍后使用 XmlReader,从(过滤的)XmlDocument 创建 XmlReader 没有问题。有很多方法可以实现您正在寻找的东西,对我来说,摆脱不需要的节点似乎是个好主意,然后开始迭代剩下的节点。
猜你喜欢
  • 1970-01-01
  • 2013-02-27
  • 2016-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-19
相关资源
最近更新 更多