XmlDictionaryReader正在读取固定大小为零的流



有人能给我解释一下为什么必须失败吗?

const int bufferSize = 2 * 1024, testValue = 123456;
var buffer = new byte[bufferSize];
var serializer = new DataContractSerializer(typeof(int));
//Serialize value
using (var memStream = new MemoryStream(buffer))
using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(memStream))
    serializer.WriteObject(writer, testValue);
//Deserialize value
using (var memStream = new MemoryStream(buffer))
using (XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(memStream, XmlDictionaryReaderQuotas.Max))
{
    object deserializedValue = serializer.ReadObject(reader); // <--- nope, this throws System.Runtime.Serialization.SerializationException: The input source is not correctly formatted.
    Console.WriteLine(deserializedValue);
}

我在玩共享内存IPC,其中一部分意味着你必须处理固定大小的进程间缓冲区。我正在将对象序列化到缓冲区中,我想表现得很酷,所以我尝试使用DataContractSerializer+binary XmlDictionaryWriter组合,这是我所知道的最快的非自定义序列化技术之一。问题是,在反序列化时,XmlDictionaryReader似乎试图将整个内存流视为一个大的xml文档,并读取它自己的流结束/块标记,遇到一大堆零,并简单地将自己丢弃。BinaryFormatter没有这个问题,因为它逐块读取流。我不得不想出一个相当蹩脚的解决方案来实现自定义流,在到达第一个0(假设是XmlDictionaryWriter的eof标记)后"伪造"流的末尾。

完整演示:

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;
namespace SerializationTest
{
    public static class Program
    {
        public static void Main()
        {
            const int bufferSize = 2 * 1024, testValue = 123456;
            var buffer = new byte[bufferSize];
            var serializer = new DataContractSerializer(typeof(int));
            //Serialize value
            using (var memStream = new MemoryStream(buffer))
            using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(memStream))
                serializer.WriteObject(writer, testValue);
            //Deserialize value
            using (var memStream = new MemoryStream(buffer))
            using (XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(memStream, XmlDictionaryReaderQuotas.Max))
            {
                object deserializedValue = serializer.ReadObject(reader); // <--- nope, this throws System.Runtime.Serialization.SerializationException: The input source is not correctly formatted.
                Console.WriteLine(deserializedValue);
            }
            //Deserialize value via FakeEndStream
            using (var memStream = new FakeEndStream(new MemoryStream(buffer)))
            using (XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(memStream, XmlDictionaryReaderQuotas.Max))
            {
                object deserializedValue = serializer.ReadObject(reader);
                Console.WriteLine(deserializedValue);
            }
        }
        private sealed class FakeEndStream : Stream
        {
            private readonly Stream _source;
            private bool _endOfStream;
            public FakeEndStream(Stream source)
            {
                _source = source;
            }
            #region The workaround
            public override int Read(byte[] buffer, int offset, int count)
            {
                int i = 0;
                for (int position = offset; i < count; i++, position++)
                {
                    int value = ReadByte();
                    if (value < 0)
                        return i;
                    buffer[position] = (byte)value;
                }
                return i;
            }
            public override int ReadByte()
            {
                if (_endOfStream)
                    return -1;
                int value = _source.ReadByte();
                if (value <= 0)
                    _endOfStream = true;
                return value;
            }
            #endregion
            #region Boilerplate overrides of Stream
            protected override void Dispose(bool disposing)
            {
                _source.Dispose();
            }
            public override void Flush()
            {
                _source.Flush();
            }
            public override long Seek(long offset, SeekOrigin origin)
            {
                return _source.Seek(offset, origin);
            }
            public override void SetLength(long value)
            {
                _source.SetLength(value);
            }
            public override void Write(byte[] buffer, int offset, int count)
            {
                _source.Write(buffer, offset, count);
            }
            public override bool CanRead
            {
                get { return _source.CanRead; }
            }
            public override bool CanSeek
            {
                get { return _source.CanSeek; }
            }
            public override bool CanWrite
            {
                get { return _source.CanWrite; }
            }
            public override long Length
            {
                get { return _source.Length; }
            }
            public override long Position
            {
                get { return _source.Position; }
                set { _source.Position = value; }
            }
            #endregion
        }
    }
}

当然,我会帮你解决这个问题(因为没有其他人愿意确认/否认症状)。

我发现问题确实在于读者阅读了无效的数据。然而,您可以通过在流的末尾写一个空白来帮助它,比如:

//Serialize value
using (var memStream = new MemoryStream(buffer))
using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(memStream))
{
    serializer.WriteObject(writer, testValue);
    writer.WriteWhitespace(" ");
}
//Deserialize value
using (var memStream = new MemoryStream(buffer))
using (XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(memStream, XmlDictionaryReaderQuotas.Max))
{
    object deserializedValue = serializer.ReadObject(reader); // o/
    Console.WriteLine(deserializedValue);
}

真不敢相信没有人会这么喜欢这个问题,因为它可能会导致运行时难以调试异常,从而使使用XmlDictionaryWriter序列化对象有点不可靠。

连接报告:https://connect.microsoft.com/VisualStudio/feedback/details/811170/xmlbinaryreader-not-able-to-read-from-fixed-size-buffer

您需要添加Flush()命令:

//Serialize value
using (var memStream = new MemoryStream(buffer))
using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(memStream))
{
    serializer.WriteObject(writer, testValue);
    writer.Flush();
}

来源:https://msdn.microsoft.com/ru-ru/library/ms752244(v=vs.110).aspx?f=255&MSPP错误=-2147217396

相关内容

  • 没有找到相关文章

最新更新