XmlDictionaryReader读取固定大小的归零流 [英] XmlDictionaryReader reading fixed-size zeroed Stream

查看:84
本文介绍了XmlDictionaryReader读取固定大小的归零流的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有人可以给我很好的解释为什么必须失败吗?

Can someone give me good explanation why this has to fail?

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 +二进制XmlDictionaryWriter组合,这是我所知道的最快的非定制序列化技术之一。
问题是在反序列化时,似乎XmlDictionaryReader试图将整个内存流视为一个大型xml文档,并读取它自己的流结束/块标记,遇到大量的零,并只是弄皱了自己。 BinaryFormatter不会出现此问题,因为它会逐块读取流。
我不得不想出一个相当la脚的解决方案,以实现自定义流,该流在到达第一个0(假定为XmlDictionaryWriter的标志)后伪造流的末尾。

I'm playing with shared memory IPC and part of it means you have to deal with fixed size inter-process buffer. I'm serializing objects into the buffer and I wanted to be cool so I tried to use DataContractSerializer + binary XmlDictionaryWriter combo, which is one of the fastest non-custom serialization techniques I know of. Problem is when deserializing, it seems XmlDictionaryReader is trying to treat entire memory stream as a big xml document and reads past it's own end-of-stream/block mark, encounters big pile of zeroes and simply craps itself. BinaryFormatter doesn't have this problem as it reads the stream block by block. I had to come up with rather lame solution of implementing custom stream that "fakes" the end of stream after reaching first 0 (assumed to be eof mark of XmlDictionaryWriter).

完整演示:

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
        }
    }
}


推荐答案

当然,我会帮您解决这个问题(因为没有人可以打扰甚至确认/否认症状)。

Sure I'll help you out with this (since no one else could be bothered to even confirm/deny the symptoms).

我发现问题确实是读者在读取无效数据。但是,您可以通过在流末尾写一个空格来帮助它,例如:

I've found the problem indeed being the reader reading invalid data. However you can help it by writing a whitespace at the end of stream, like so:

//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序列化对象有点不可靠。

Can't believe no one so much as favorited this question as it potentially leads to hard to debug exceptions at runtime and is thus making serializing objects with XmlDictionaryWriter a wee bit unreliable.

连接报告: https://connect.microsoft.com/VisualStudio/feedback/details / 811170 / xmlbinaryreader无法从固定大小的缓冲区读取

这篇关于XmlDictionaryReader读取固定大小的归零流的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆