请不要将此标记为重复,我已尝试过类似问题中的建议,但无法解决该错误。
我在尝试反序列化和 ReadObject 时遇到以下错误:
System.Runtime.Serialization.SerializationException:'反序列化 SampleClass 类型的对象时出错。根级别的数据无效。第 1 行,位置 1。'
在下面添加我的示例代码:
using System.IO.MemoryMappedFiles;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
namespace TestMigration
{
public class Program
{
[SupportedOSPlatform("windows")]
public static void Main(string[] args)
{
using (var mmf = MemoryMappedFile.CreateNew("TempName", 10240, MemoryMappedFileAccess.ReadWrite))
{
WriteToMemoryMappedFile(mmf);
var infoObj = ReadFromMemoryMappedFile(mmf);
Console.WriteLine(infoObj?.Name);
}
}
private static void WriteToMemoryMappedFile(MemoryMappedFile mmf)
{
var serializer = new DataContractSerializer(typeof(SampleClass));
var infoObj = new SampleClass { Name = "TestApp" };
using (var stream = mmf.CreateViewStream(0, 0, MemoryMappedFileAccess.Write))
{
serializer.WriteObject(stream, infoObj);
stream.Flush();
}
}
private static SampleClass? ReadFromMemoryMappedFile(MemoryMappedFile mmf)
{
var serializer = new DataContractSerializer(typeof(SampleClass));
using (var stream = mmf.CreateViewStream(0, 0, MemoryMappedFileAccess.Read))
{
stream.Position = 0;
return (SampleClass?)serializer.ReadObject(stream);
}
}
}
}
[DataContract]
public class SampleClass
{
[DataMember]
public string? Name { get; set; }
}
我尝试添加解决方法,例如使用 XmlReader、XmlDictionaryReader 等,但没有成功。
您的问题是,创建的内存映射文件具有固定大小[1]。 因此,在将 XML 写入视图流后,该文件将包含 XML 以及足够的未初始化字节,以将长度填充到固定长度 10240。您可以通过在
stream.Length
中打印 WriteToMemoryMappedFile()
的值来确认这一点:
using (var stream = mmf.CreateViewStream(0, 0, MemoryMappedFileAccess.Write))
{
Debug.WriteLine(stream.Length); // Prints 10240
由于尾部填充,您的内存映射文件是一个“格式错误的 XML 文档”。 由 XmlReader
内部创建的
DataContractSerializer.ReadObject()
旨在通过读取根对象的末尾来验证这一点,以确保整个文件实际上格式良好。因为它不是,所以你会得到你看到的异常。那么您有哪些解决方法可供选择?
首先,您可以将内存映射文件视为 XML 片段序列,并仅读取第一个片段。 首先介绍一下下面的扩展方法:
public static partial class DataContractSerializerExtensions
{
public static IEnumerable<T?> ReadObjectFragments<T>(Stream stream, DataContractSerializer? serializer = null, bool closeInput = true)
{
var settings = new XmlReaderSettings
{
ConformanceLevel = ConformanceLevel.Fragment,
CloseInput = closeInput,
};
serializer ??= new DataContractSerializer(typeof(T));
using (var outerReader = XmlReader.Create(stream, settings))
{
while (outerReader.Read())
{ // Skip whitespace
if (outerReader.NodeType == XmlNodeType.Element)
using (var innerReader = outerReader.ReadSubtree())
{
yield return (T?)serializer.ReadObject(innerReader);
}
}
}
}
}
然后修改
ReadFromMemoryMappedFile()
如下:
private static SampleClass? ReadFromMemoryMappedFile(MemoryMappedFile mmf)
{
using (var stream = mmf.CreateViewStream(0, 0, MemoryMappedFileAccess.Read))
{
return DataContractSerializerExtensions.ReadObjectFragments<SampleClass>(stream).FirstOrDefault();
}
}
您的
SampleClass
现在将成功反序列化,因为对
FirstOrDefault()
的调用与 ConformanceLevel.Fragment
的使用相结合,可以防止底层 XmlReader
尝试读取初始片段之外的内容。演示小提琴#1 这里其次,由于您的内存映射文件无论如何都不是格式良好的 XML,因此您可以考虑采用“消息框架”方法,在 XML 内容之前写入实际数据大小。
为此,首先创建以下通用方法:
public static partial class MemoryMappedFileExtensions
{
static readonly int LongSize = Marshal.SizeOf<long>();
public static void WriteAndFrame<T>(this MemoryMappedFile mmf, T value, long offset = 0, DataContractSerializer? serializer = null)
{
long actualSize;
serializer ??= new DataContractSerializer(typeof(T));
// Write the data contract data at offset 8.
using (var stream = mmf.CreateViewStream(checked(offset + LongSize), 0, MemoryMappedFileAccess.Write))
{
var startPosition = stream.Position;
serializer.WriteObject(stream, value);
stream.Flush();
actualSize = stream.Position - startPosition;
}
// Write the message frame size at offset 0.
using (var accessor = mmf.CreateViewAccessor(offset, LongSize))
{
accessor.Write(0, actualSize);
}
}
public static T? ReadFromFrame<T>(this MemoryMappedFile mmf, long offset = 0, DataContractSerializer? serializer = null)
{
long actualSize;
serializer ??= new DataContractSerializer(typeof(T));
// Read the message frame size from offset zero.
using (var accessor = mmf.CreateViewAccessor(offset, LongSize))
{
accessor.Read(0, out actualSize);
}
// Read the XML starting at offset 8 with the specified message frame size.
using (var stream = mmf.CreateViewStream(offset + LongSize, actualSize, MemoryMappedFileAccess.Read))
{
return (T?)serializer.ReadObject(stream);
}
}
}
现在你的读写方法可以重写如下:
private static void WriteToMemoryMappedFile(MemoryMappedFile mmf)
{
var infoObj = new SampleClass { Name = "TestApp" };
mmf.WriteAndFrame(infoObj);
}
private static SampleClass? ReadFromMemoryMappedFile(MemoryMappedFile mmf) =>
mmf.ReadFromFrame<SampleClass>();
演示小提琴 #2
这里。
最后,您可能会考虑使用内存映射文件来保存单个 XML 文档的方法是否理想,因为文件大小必须提前固定。
[1]