将 2 个 XDocuments 保存到一个流或从一个流加载 2 个 XDocuments

发布于 2024-11-08 04:47:58 字数 1441 浏览 2 评论 0原文

我有 2 个 XDocuments。一种是一些元数据,另一种是大量数据。

在 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 statements 时,问题就出现了。 XML 声明必须是文档中的第一个节点,并且前面不允许出现空格字符。第 18 行,位置 14。

任何帮助将不胜感激。

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

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.

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.

I get a file as below:

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

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.

Any help would be greatly appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

影子的影子 2024-11-15 04:47:58

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

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

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.

似狗非友 2024-11-15 04:47:58

我最终编写了自己的 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();
    }
}

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();
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文