C# - 解析XElement时的奇怪行为

时间:2011-01-14 11:34:05

标签: c# parsing xelement

我通过将字符串解析为XElement来观察到一种奇怪的行为。

首先,这是我要解析的XML:

<return value="0">
    <resultset>
        <meta>
            <column type="char(30)"></column>
        </meta>
        <datarow>
            <datacol>
                <![CDATA[master]]>
            </datacol>
        </datarow>
    </resultset>
</return>

现在代码:

            try
            {
                xmlResult = XElement.Parse(
                    _UTF8Encoder.GetString(_responseBytes.GetBuffer(), 0, (int)_responseBytes.Length),
                    LoadOptions.PreserveWhitespace);
            }
            catch
            {
                xmlResult = XElement.Parse(
                   _UTF8Encoder.GetString(_responseBytes.GetBuffer(), 0, (int)_responseBytes.Length),
                   LoadOptions.PreserveWhitespace);
            }

我在try和catch块中做了同样的事情。

有时try块会引发XmlException(“Root元素丢失。”),但catch块(执行完全相同的事情)不会抛出它并正确解析字符串。

有人可以告诉我为什么吗?

谢谢!

[编辑]

以下是整个方法代码:

private TcpClient _client;
private NetworkStream _clientStream;
private MemoryStream _responseBytes;
private readonly UTF8Encoding _UTF8Encoder = new UTF8Encoding();
private const int BUFFER_SIZE = 1024;

    private XElement Receive()
    {
        byte[] buffer = new byte[BUFFER_SIZE];

        XElement xmlResult;
        Encoding serverEncoding = this.Task.Server.Encoding;

        // Reading result
        while (true)
        {
            _responseBytes = new MemoryStream();

            try
            {
                IAsyncResult e = _clientStream.BeginRead(buffer,
                    0,                                              // Begin
                    BUFFER_SIZE,                                    // Length
                    new AsyncCallback(OnBeginRead),                 // Callback used
                    new SocketAsyncState(_clientStream, buffer));   // Passing buffer to callback

                e.AsyncWaitHandle.WaitOne();    // Wait until data are in pipe

                if (((SocketAsyncState)e.AsyncState).HasError)
                {
                    throw new ObjectDisposedException();
                }

                // Try to convert to a XElement, if fail, redo all process.
                _responseBytes.Position = 0;

                try
                {
                    xmlResult = XElement.Parse(
                        _UTF8Encoder.GetString(_responseBytes.GetBuffer(), 0, (int)_responseBytes.Length),
                        LoadOptions.PreserveWhitespace);
                }
                catch
                {
                    xmlResult = XElement.Parse(
                       _UTF8Encoder.GetString(_responseBytes.GetBuffer(), 0, (int)_responseBytes.Length),
                       LoadOptions.PreserveWhitespace);
                }

                // Result 100% retrieved : quit loop
                break;
            }
            catch (Exception ex)
            {

                if (ex is ObjectDisposedException
                    || ex is XmlException)
                {
                    while (!IsConnected) { Wait(); }   // Wait that the network comes back
                    SendSyn();                         // Relaunch process
                }
            }
        }

        // Result 100% retrieved : send ACK to Socket
        SendAck();

        return xmlResult;
    }

    private void OnBeginRead(IAsyncResult ar)
    {
        SocketAsyncState state = ar.AsyncState as SocketAsyncState;
        byte[] nextBuffer = new byte[BUFFER_SIZE];
        int numberOfBytesReaded;
        Encoding serverEncoding = this.Task.Server.Encoding;

        try
        {
            numberOfBytesReaded = state.Stream.EndRead(ar);
        }
        catch(Exception)
        {
            ((SocketAsyncState)ar.AsyncState).HasError = true;
            // Quit
            return;
        }

        // While data are available, read next buffer (recursive call to this method)
        if (state.Stream.DataAvailable && state.Stream.CanRead)
        {
            state.Stream.BeginRead(nextBuffer,
                0,
                BUFFER_SIZE,
                new AsyncCallback(OnBeginRead),
                new SocketAsyncState(state.Stream, nextBuffer));
        }

        // Default C# strings are in UTF-8, so convert stream only if needed
        if (serverEncoding.CodePage != _UTF8Encoder.CodePage)
        {
            byte[] buffer = Encoding.Convert(serverEncoding,
                _UTF8Encoder,
                state.Data.TakeWhile((b) => b != '\0').ToArray());

            _responseBytes.Write(buffer, 0, buffer.Length);
        }
        else
        {
            _responseBytes.Write(state.Data, 0, numberOfBytesReaded);

        }
    } 

1 个答案:

答案 0 :(得分:3)

你还没有向我们展示_responseBytes是什么,但有一个建议让人想起:如果它是异步填充的(例如通过异步Web请求),那么第一次尝试可能会在数据出现之前发生,但是当catch块执行时,数据已经到达。

(显然如果是这种情况,解决方案是使用这种catch块但是要修复时间。)

编辑:好的,我想我可能会看到问题所在。就在这里:

e.AsyncWaitHandle.WaitOne();

我怀疑它会等到读取在套接字级别发生,但在调用回调之前。您的代码假设它等待回调完成

所发生的事情(这仍然只是猜测)是:

  • 在主线程上,启动操作并等待其完成
  • 读取数据
  • WaitOne()在主线程中返回,同时在线程池线程上调用回调
  • 您尝试解析主线程中的内存流中的数据...
  • ...和然后回调实际上将数据写入内存流
  • ...和然后你有第二次尝试解析,因为现在数据已经成功了