【问题标题】:Save/load 2 XDocuments to/from one stream在一个流中保存/加载 2 个 XDocuments
【发布时间】:2011-05-16 12:10:57
【问题描述】:

我有 2 个 XDocument。一个是一些元数据,一个是很多数据。

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

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

目前我正在保存/加载如下:

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.");
    }
}

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

我得到一个文件如下:

<?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.

任何帮助将不胜感激。

【问题讨论】:

  • XDocument.Load(ObjectStream) 已经返回了一个 XDocument 对象,不需要实例化一个new XDocument

标签: c# xml stream linq-to-xml xbox360


【解决方案1】:

XML 声明 (&lt;?xml version=....?&gt;) 只能在文档开头出现一次 - 这就是您看到的错误。您还需要一个根节点,因此您不能以这种方式将两个不同的文档一起序列化到一个文件下。如果您修复了 XML 声明,这可能是您遇到的下一个错误。

如果您想从一个文件中保存和加载文档,则需要将它们组合成一个文档进行序列化/反序列化。

【讨论】:

  • 我想避免将它们合并到一个文档中,因为我希望能够仅加载第一部分(元数据)。
【解决方案2】:

我最终编写了自己的 Stream,可以将其视为多流。它允许您将一个流连续视为多个流。即将一个多流传递给一个 xml 解析器(或其他任何东西),我会读到一个标记,上面写着“这是流的结尾”。如果您随后将相同的流传递给另一个 xml 解析器,它将从该标记读取到下一个或 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();
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多