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限制)。异步方法对于我的用例并不重要。