C# 从内存中读取XML<;字节>;

C# 从内存中读取XML<;字节>;,c#,.net,.net-core,C#,.net,.net Core,我有一个内存,它保存XML元素的二进制数据。我想将XML元素读入XElement对象,但这似乎比乍一看要容易。当使用字节[]时,我会这样做: public static XElement GetXElementFromByteArray(byte[] buffer) { using var stream = new MemoryStream(buffer); using var xmlReader = XmlReader.Create(stream); return XElemen

我有一个
内存
,它保存XML元素的二进制数据。我想将XML元素读入
XElement
对象,但这似乎比乍一看要容易。当使用
字节[]
时,我会这样做:

public static XElement GetXElementFromByteArray(byte[] buffer)
{
  using var stream = new MemoryStream(buffer);
  using var xmlReader = XmlReader.Create(stream);

  return XElement.Load(xmlReader);
}

不幸的是,我无法从
内存
Span
对象创建
对象,
XmlReader
需要流对象。当然,我可以将
内存
转换为字节数组,但这将复制数据,我的优化目标是减少集合。

实际上没有一个预先封装的
实现(或
XmlReader
实现)可以方便地从
内存
工作。如果打算使用
MemoryStream
,那么最好的办法可能是尝试将其作为数组获取,如果不是,则强制创建池副本:

ArraySegment段=默认值;
bool=false;
尝试
{
如果(!MemoryMarshal.TryGetArray(内存,输出段))
{
var arr=ArrayPool.Shared.Rent(memory.Length);
内存.CopyTo(arr);
段=新数组段(arr,0,memory.Length);
租赁=真实;
}
使用(var ms=new MemoryStream(segment.Array、segment.Offset、segment.Count))
{
//…你的用法在这里!
使用(var xmlReader=xmlReader.Create(ms))
{
返回XElement.Load(xmlReader);
}
}
}
最后
{
if(租赁)ArrayPool.Shared.Return(segment.Array);
}

另一种方法是创建一个新的
实现,该实现可针对
内存
。这是一个很大的工作。

我已经提出了一个自定义流,似乎可以工作,但我想知道是否有一个“官方”实现。我知道这只适用于
内存
,而不适用于泛型类型,但通常内存访问是以一个字节为基础的

这是我的实现:

public class ReadonlyMemByteStream : Stream
{
    private readonly ReadOnlyMemory<byte> _buffer;
    private int _offset;

    public ReadonlyMemByteStream(ReadOnlyMemory<byte> buffer)
    {
        _buffer = buffer;
        _offset = 0;
    }

    public override void Flush()
    {
        // NOP
    }

    public override int ReadByte()
    {
        return _buffer.Span[_offset++];
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        var bufferLength = _buffer.Length - _offset;
        if (count > bufferLength)
            count = bufferLength;
        _buffer.Span.Slice(_offset).CopyTo(new Span<byte>(buffer, offset, count));
        _offset += count;
        return count;
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        throw new NotSupportedException();
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        switch (origin)
        {
            case SeekOrigin.Begin:
                _offset = (int)offset;
                break;
            case SeekOrigin.Current:
                _offset += (int)offset;
                break;
            case SeekOrigin.End:
                _offset = _buffer.Length + (int)offset;
                break;
            default:
                throw new ArgumentOutOfRangeException(nameof(origin), origin, null);
        }
        return _offset;
    }

    public override void SetLength(long value) => throw new NotSupportedException();

    public override bool CanRead => true;
    public override bool CanSeek => true;
    public override bool CanWrite => false;

    public override long Length => _buffer.Length;

    public override long Position
    {
        get => _offset;
        set => _offset = (int)value;
    }
}

public class MemByteStream : ReadonlyMemByteStream
{
    private readonly Memory<byte> _buffer;

    public MemByteStream(Memory<byte> buffer) : base(buffer)
    {
        _buffer = buffer;
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        new Span<byte>(buffer, offset, count).CopyTo(_buffer.Span.Slice((int)Position));
        Position += count;
    }

    public override bool CanWrite => true;
}
public类readonlymmbytestream:Stream
{
专用只读内存缓冲区;
私人国际单位抵销;
公共ReadOnlyMemberStream(ReadOnlyMemory缓冲区)
{
_缓冲区=缓冲区;
_偏移量=0;
}
公共覆盖无效刷新()
{
//不
}
公共覆盖int ReadByte()
{
返回_buffer.Span[_offset++];
}
公共重写整型读取(字节[]缓冲区、整型偏移量、整型计数)
{
var bufferLength=\u buffer.Length-\u offset;
如果(计数>缓冲区长度)
计数=缓冲长度;
_buffer.Span.Slice(_offset).CopyTo(新Span(buffer,offset,count));
_偏移量+=计数;
返回计数;
}
公共重写无效写入(字节[]缓冲区、整数偏移量、整数计数)
{
抛出新的NotSupportedException();
}
公共覆盖长寻道(长偏移,参见原始坐标系)
{
开关(原点)
{
案例请参见Korigin。开始:
_偏移量=(int)偏移量;
打破
案例见Korigin.Current:
_偏移量+=(int)偏移量;
打破
案例请参见Korigin。结束:
_偏移量=_buffer.Length+(int)偏移量;
打破
违约:
抛出新ArgumentOutOfRangeException(nameof(origin),origin,null);
}
返回偏移量;
}
public override void SetLength(长值)=>抛出新的NotSupportedException();
public override bool CanRead=>true;
public override bool CanSeek=>true;
公共覆盖boolcanwrite=>false;
公共覆盖长长度=>\u buffer.Length;
公众优先多头仓位
{
get=>\u偏移量;
set=>_offset=(int)值;
}
}
公共类MemByTestStream:ReadonlyMemByteStream
{
专用只读存储器_缓冲区;
公共MemByteStream(内存缓冲区):基(缓冲区)
{
_缓冲区=缓冲区;
}
公共重写无效写入(字节[]缓冲区、整数偏移量、整数计数)
{
新Span(缓冲区、偏移量、计数).CopyTo(_buffer.Span.Slice((int)位置));
位置+=计数;
}
public override bool CanWrite=>true;
}

看起来很合理;您可能需要在
搜索
位置
(设置)中进行范围验证。您可能还希望在
MemByteStream
中实现
WriteByte
,并且为了提高性能:请注意,在最近的运行时(可能只是netcoreapp2.1和更高版本?),存在
Read[Async]
Write[Async]
的重载,这需要span/memory/etc来调用
span
重写是一个好主意。我已经在内部使用了跨距,这样可以节省一些额外的周期。我不是故意检查边界的。底层类做了,我不想做过多的检查(由于CPU限制)。异步方法对于我的用例并不重要。