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

查看:30
本文介绍了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 没有这个问题,因为它逐块读取流.我不得不想出相当蹩脚的解决方案来实现自定义流,在达到第一个 0 后伪造"流的结尾(假设是 XmlDictionaryWriter 的 eof 标记).

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-not-able-to-read-from-fixed-size-buffer

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

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