【问题标题】:Processing badly formatted XML in .NET3.5在 .NET3.5 中处理格式错误的 XML
【发布时间】:2011-06-23 13:41:29
【问题描述】:

给定一个通过 TCP 将 XML 流式传输给我的第三方系统。总传输的 XML 内容(不是流中的一条消息,而是串联的消息)如下所示:

   <root>
      <insert ....><remark>...</remark></insert>
      <delete ....><remark>...</remark></delete>
      <insert ....><remark>...</remark></insert>
      ....
      <insert ....><remark>...</remark></insert>
   </root>

上述示例的每一行都可以单独处理。由于这是一个流式处理,我不能等到一切都到了,我必须在内容到来时对其进行处理。问题是内容块可以按任意点切片,不尊重任何标签。 如果内容以这样的片段形式到达,您对如何处理内容有什么好的建议吗?

块 1:

  <root>
      <insert ....><rem

块 2:

                      ark>...</remark></insert>
      <delete ....><remark>...</remark></delete>
      <insert ....><remark>...</rema

块 N:

                                    rk></insert>
      ....
      <insert ....><remark>...</remark></insert>
   </root>

编辑:

虽然处理速度不是问题(没有实时问题),但我不能等待整个消息。实际上最后一块永远不会到达。第三方系统在遇到变化时发送消息。这个过程永远不会结束,它是一个永不停止的流。

【问题讨论】:

  • 你需要实时处理还是等到你得到完整的内容?换句话说,这个问题是关于以最优雅/最有效的方式处理 XML 片段或连接流中的数据吗?
  • 我认为如果您可以使用阻塞读取,您可以使用XmlReader 类。不知道如何使用非阻塞 IO 来做到这一点。
  • 听起来你需要使用一些字符串操作,这样当你收到一个块时,你取出可处理的部分,处理它们(如果可以的话,异步会很好)然后将下一个块附加到剩下的任何部分,然后像这样循环。

标签: .net xml streaming non-well-formed


【解决方案1】:

我对这个问题的第一个想法是创建一个简单的 TextReader 衍生物,负责缓冲来自流的输入。然后,此类将用于提供 XmlReader。 TextReader 派生类可以相当容易地扫描传入内容以查找 XML 的完整“块”(带有开始和结束括号的完整元素、文本片段、完整属性等)。它还可以向调用代码提供一个标志,以指示一个或多个“块”何时可用,以便它可以从 XmlReader 请求下一个 XML 节点,这将触发从 TextReader 派生类发送该块并将其从缓冲区中删除.

编辑:这是一个快速而肮脏的例子。我不知道它是否完美运行(我没有测试过),但它传达了我试图传达的想法。

public class StreamingXmlTextReader : TextReader
{
    private readonly Queue<string> _blocks = new Queue<string>();
    private string _buffer = String.Empty;
    private string _currentBlock = null;
    private int _currentPosition = 0;

    //Returns if there are blocks available and the XmlReader can go to the next XML node
    public bool AddFromStream(string content)
    {
        //Here is where we would can for simple blocks of XML
        //This simple chunking algorithm just uses a closing angle bracket
        //Not sure if/how well this will work in practice, but you get the idea
        _buffer = _buffer + content;
        int start = 0;
        int end = _buffer.IndexOf('>');
        while(end != -1)
        {
            _blocks.Enqueue(_buffer.Substring(start, end - start));
            start = end + 1;
            end = _buffer.IndexOf('>', start);
        }

        //Store the leftover if there is any
        _buffer = end < _buffer.Length
            ? _buffer.Substring(start, _buffer.Length - start) : String.Empty;

        return BlocksAvailable;
    }

    //Lets the caller know if any blocks are currently available, signaling the XmlReader can ask for another node
    public bool BlocksAvailable { get { return _blocks.Count > 0; } }

    public override int Read()
    {
        if (_currentBlock != null && _currentPosition < _currentBlock.Length - 1)
        {
            //Get the next character in this block
            return _currentBlock[_currentPosition++];
        }
        if(BlocksAvailable)
        {
            _currentBlock = _blocks.Dequeue();
            _currentPosition = 0;
            return _currentBlock[0];
        }
        return -1;
    }
}

【讨论】:

    【解决方案2】:

    经过进一步调查,我们发现 XML 流已被 TCP 缓冲区分割,只要它已满。因此,切片实际上是在字节流中随机发生的,即使在 unicode 字符内也会造成切割。 因此,我们必须在字节级别组装这些部分并将其转换回文本。如果转换失败,我们等待下一个字节块,然后再试一次。

    【讨论】:

      猜你喜欢
      • 2015-04-19
      • 1970-01-01
      • 1970-01-01
      • 2015-09-19
      • 1970-01-01
      • 1970-01-01
      • 2012-06-04
      • 1970-01-01
      • 2021-03-01
      相关资源
      最近更新 更多