从一个流中保存/加载2个XDocument [英] Save/load 2 XDocuments to/from one stream

查看:101
本文介绍了从一个流中保存/加载2个XDocument的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有2个XDocuments.一个是一些元数据,另一个是很多数据.

I've got 2 XDocuments. One is some meta data, the other is a lot of data.

在Xbox(XNA)上,我希望能够将两者都保存到文件流中,首先是元数据XDoc,然后是实际数据XDoc.

On the Xbox (XNA), I'd like to be able to save both to a file stream, meta data XDoc first, then the actual data XDoc.

然后,我希望能够仅访问元数据XDoc(忽略文件流的其余部分),并且还能够访问元数据XDoc和数据XDoc.

I'd then like to be able to access just the meta data XDoc (ignoring the rest of the file stream), and also to be able to access the meta data XDoc and the data XDoc.

当前,我正在按以下方式进行保存/加载:

Currently i'm saving/loading as follows:

public void Serialise(Stream SaveStream, object Obj)
{
    XDocument XDoc = new XDocument(new XElement(@"SaveData", new XAttribute(@"Version", @"1.0"),
                                                GetXMLElement(Obj)));

    XDoc.Save(SaveStream);
}

public object Deserialise(Stream ObjectStream)
{
    XDocument XDoc = XDocument.Load(ObjectStream); // Error line

    switch (XDoc.Element(@"SaveData").Attribute(@"Version").Value)
    {
        case @"1.0":
            return GetObject(XDoc.Element(@"SaveData").FirstNode as XElement);
        default:
            throw new NotSupportedException("This save file version (" + XDoc.Element(@"SaveData").Attribute(@"Version").Value +
                                            " is not supported, please upgrade your game.");
    }
}

要保存元数据和实际数据,我只是在同一流上两次调用序列化.

To save meta data followed by actual data i'm just calling serialise twice on the same stream.

我得到一个如下文件:

<?xml version="1.0" encoding="utf-8"?>
<SaveData Version="1.0">
    ....
</SaveData><?xml version="1.0" encoding="utf-8"?>
<SaveData Version="1.0">
    ....
</SaveData>

当我尝试阅读第一个XDoc时出现问题:Unexpected XML declaration. The XML declaration must be the first node in the document, and no white space characters are allowed to appear before it. Line 18, position 14.

The problem comes when i try and read the first XDoc: Unexpected XML declaration. The XML declaration must be the first node in the document, and no white space characters are allowed to appear before it. Line 18, position 14.

任何帮助将不胜感激.

推荐答案

我最终编写了自己的Stream,可以将其视为多流.它使您可以将一个流连续地视为多个流.也就是说,将多数据流传递到xml解析器(或其他任何工具),然后我将读取一个标记,该标记表示这是数据流的末尾".然后,如果将相同的流传递给另一个xml解析器,它将从该标记读取到下一个标记或EOF:

I ended up writing my own Stream, which can be thought of as a multistream. It allows you to treat one stream as multiple stream in succession. i.e. pass a multistream to an xml parser (or anything else) and i'll read up to a marker, which says 'this is the end of the stream'. If you then pass that same stream to another xml parser, it'll read from that marker, to the next one or EOF:

public class MultiStream : Stream
{
    private readonly byte[] _RandomBytes = "410801dd-6f14-4d68-8e0e-29686d212cb2".Select(c => (byte)c).ToArray();

    private Queue<byte> _RollingBytesRead;

    private Stream _UnderlyingStream;

    private bool UnderlyingEOF = false;
    private bool EOFMarker = false;
    private int BufferedBytesToRead = 0;

    public MultiStream(Stream UnderlyingStream)
        : base()
    {
        _UnderlyingStream = UnderlyingStream;
        _RollingBytesRead = new Queue<byte>(_RandomBytes.Length);
    }

    public override bool CanRead
    {
        get { return !UnderlyingEOF || _UnderlyingStream.CanRead; }
    }

    public override bool CanSeek
    {
        get { return false; }
    }

    public override bool CanWrite
    {
        get { return _UnderlyingStream.CanWrite; }
    }

    public override void Flush()
    {
        _UnderlyingStream.Flush();
    }

    public override long Length
    {
        get { throw new NotSupportedException(); }
    }

    public override long Position
    {
        get
        {
            throw new NotSupportedException();
        }
        set
        {
            throw new NotSupportedException();
        }
    }

    public override int ReadByte()
    {
        if (EOFMarker)
            return -1;

        // This should read the next byte from the underlying stream, check for the random bytes EOF marker, then return the next byte from the buffer

        // If our buffer is smaller than the random bytes and we've not hit the EOF, then we need to fill it
        while (!UnderlyingEOF && _RollingBytesRead.Count < _RandomBytes.Length)
        {
            int BytesRead = _UnderlyingStream.ReadByte();
            if (BytesRead == -1)
            {
                UnderlyingEOF = true;
                BufferedBytesToRead = _RollingBytesRead.Count;
            }
            else
            {
                _RollingBytesRead.Enqueue((byte)BytesRead);
            }
        }

        if (EncounteredEndOfStreamBytes()) // Now check to see if the buffer matches our EOF marker
        {
            // If it does stop now, since we don't want to output any of the EOF marker.
            BufferedBytesToRead = 0;
            _RollingBytesRead.Clear();
            EOFMarker = true;
            return -1;
        }
        else if (UnderlyingEOF) // If we've already encountered the end of the underlying stream and have a buffer,
                                // then output the next byte since it's not part of the EOF marker, it's part of the stream
        {
            if (BufferedBytesToRead != 0)
            {
                BufferedBytesToRead--;
                return _RollingBytesRead.Dequeue();
            }
            else
            {
                return -1;
            }
        }
        else
        {
            int ByteRead = _UnderlyingStream.ReadByte();

            if (ByteRead == -1)
            {
                // We've reached the end so we should output the buffer
                UnderlyingEOF = true;
                BufferedBytesToRead = _RollingBytesRead.Count;

                // Recurse once just to avoid repeating code above
                return ReadByte();
            }
            else
            {
                byte BufferedByte = _RollingBytesRead.Dequeue();
                _RollingBytesRead.Enqueue((byte)ByteRead);

                return BufferedByte;
            }
        }
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        bool EncounteredEOF = false;

        int BufferIndex = 0;

        while (offset > 0)
        {
            if (ReadByte() == -1)
            {
                EncounteredEOF = true;
            }

            offset--;
        }

        while (!EncounteredEOF && count > 0)
        {
            // Read the next byte (includes checks for our end of stream marker) and actually returns the buffered byte (not the next underlying stream read byte)
            int ByteRead = ReadByte();

            if (ByteRead == -1)
            {
                break;
            }
            else
            {
                buffer[BufferIndex] = (byte)ByteRead;

                count--;
                BufferIndex++;
            }
        }

        return BufferIndex;
    }

    private bool EncounteredEndOfStreamBytes()
    {
        if (_RollingBytesRead.Count != _RandomBytes.Length)
            return false;

        byte[] QueueArray = _RollingBytesRead.ToArray();

        for (int i = 0; i < _RandomBytes.Length; i++)
        {
            if (_RandomBytes[i] != QueueArray[i])
                return false;
        }
        return true;
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        throw new NotSupportedException();
    }

    public override void SetLength(long value)
    {
        throw new NotSupportedException();
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        _UnderlyingStream.Write(buffer, offset, count);
    }

    public void WriteStreamSeperator()
    {
        Write(_RandomBytes, 0, _RandomBytes.Length);
    }

    public void AdvanceToNextStream()
    {
        if (UnderlyingEOF)
            throw new InvalidOperationException("No more streams");

        // If we're not currently at an EOF marker, advance until we get to one.
        while (!EOFMarker)
        {
            ReadByte();
        }

        EOFMarker = false;
        _RollingBytesRead.Clear();
    }
}

这篇关于从一个流中保存/加载2个XDocument的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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