【问题标题】:Read entire elements from an XML network stream从 XML 网络流中读取整个元素
【发布时间】:2012-02-21 13:17:57
【问题描述】:

我正在用 C# .NET 4.0 编写网络服务器。有一个网络 TCP/IP 连接,我可以通过它接收完整的 XML 元素。他们定期到达,我需要立即处理。每个 XML 元素本身就是一个完整的 XML 文档,因此它有一个开始元素、几个子节点和一个结束元素。整个流没有单个根元素。所以当我打开连接时,我得到的是这样的:

<status>
    <x>123</x>
    <y>456</y>
</status>

一段时间后它继续:

<status>
    <x>234</x>
    <y>567</y>
</status>

等等。我需要一种方法来读取完整的 XML 字符串,直到状态元素完成。我不想用纯文本阅读方法来做到这一点,因为我不知道数据以什么格式到达。正如其他地方经常描述的那样,我绝对不能等到整个流完成。我尝试使用 XmlReader 类,但它的文档很奇怪,方法不起作用,第一个元素丢失,发送第二个元素后,发生 XmlException,因为有两个根元素。

【问题讨论】:

  • 我怀疑 XmlReader 是要走的路,因为它需要一个根元素并且它不适合连续流式传输 XML 元素。通常在处理基于套接字的连接时,您会监听连接并寻找识别每个数据块的开始和结束。我知道您说过您不想这样做,但我希望您必须使用纯文本读取方法来至少识别出一块您可以解析的 XML。
  • 您需要为每条消息实例化一个新的阅读器,其中消息是一个完整的节点。

标签: c# .net xml networking


【解决方案1】:

试试这个:

var settings = new XmlReaderSettings
{
    ConformanceLevel = ConformanceLevel.Fragment
};

using (var reader = XmlReader.Create(stream, settings))
{
    while (!reader.EOF)
    {
        reader.MoveToContent();

        var doc = XDocument.Load(reader.ReadSubtree());

        Console.WriteLine("X={0}, Y={1}",
            (int)doc.Root.Element("x"),
            (int)doc.Root.Element("y"));

        reader.ReadEndElement();
    }
}

【讨论】:

  • 这已经接近了。它可以读取同一流中的多个 XML 根元素,但 ReadEndElement 调用将始终阻塞,直到下一个元素进入。我可以安全地删除它还是必须等待另一个事件?
  • 如果阻塞的是 ReadEndElement 而不是 ReadSubtree,您可以在 ReadEndElement 调用之前简单地处理 XDocument。
【解决方案2】:

如果您将“一致性级别”更改为“片段”,它可能适用于XmlReader

这是来自MSDN 的(稍作修改的)示例:

XmlReaderSettings settings = new XmlReaderSettings();
settings.ConformanceLevel = ConformanceLevel.Fragment;
XmlReader reader = XmlReader.Create(streamOfXmlFragments, settings);

【讨论】:

    【解决方案3】:

    您可以使用XElement.Load,它更多地用于流式传输 .net 3.5 中新增的 Xml 元素片段,并且还支持直接从流中读取。

    看看System.Xml.Linq

    我认为您可能仍然需要添加一些控制逻辑来对您收到的消息进行分区,但您不妨试一试。

    【讨论】:

      【解决方案4】:

      我不确定是否有任何内置功能可以做到这一点。 我会打开一个字符串生成器,填充它直到看到&lt;/status&gt; 标签,然后使用普通的 XmlDocument 解析它。

      【讨论】:

      • P.S.我知道你写过你不想使用 String 方法,但我很确定这是你唯一的方法。
      【解决方案5】:

      和dtb的方案没有本质区别,但是linqier

      static IEnumerable<XDocument> GetDocs(Stream xmlStream)
      {
          var xmlSettings = new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Fragment };
          using (var xmlReader = XmlReader.Create(xmlStream, xmlSettings))
          {
              var xmlPathNav = new XPathDocument(xmlReader).CreateNavigator();
              foreach (var selectee in xmlPathNav.Select("/*").OfType<XPathNavigator>())
                  yield return XDocument.Load(selectee.ReadSubtree());
          }
      }
      

      我在 PowerShell 中遇到了类似的问题,但提问者的问题是在 C# 中,所以我尝试翻译它(并验证它是否有效)。 Here 是我找到线索的地方,让我克服了最后的小障碍(“...... XPathDocument 发挥其魔力的方式是创建一个“透明”根节点,并保存其中的片段。我说它是透明的因为您的 XPath 查询可以使用根节点轴,并且仍然可以正确解析为片段...")

      我正在使用的 XML 片段恰好很小。如果您有更大的块,您可能想要查看XStreamingElement - 它会增加很多复杂性,但在处理大量 XML 时也会大大减少内存使用量。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-06
        • 2021-03-30
        • 2016-06-26
        • 1970-01-01
        相关资源
        最近更新 更多