0

我有 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.

任何帮助将不胜感激。

4

2 回答 2

0

The XML declaration (<?xml version=....?>) can only appear once at the beginning of the document - that is the error you are seeing. You also need a root node, so you can't serialize two different documents together under one file in this manner. If you fixed the XML declaration, this would probably be the next error you get.

If you want to save and load the documents from one file, then you need to combine them into a single document for serialization/deserialization.

于 2011-05-16T12:17:17.523 回答
0

我最终编写了自己的 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();
    }
}
于 2011-05-17T09:36:34.097 回答