【问题标题】:Appending to a serialized collection附加到序列化集合
【发布时间】:2010-12-14 01:34:36
【问题描述】:

我有某种类型的序列化数组。 有没有办法将新对象附加到这个序列化数组(以序列化形式)而不需要将已经保存的集合读入内存?

例子:

我有一个 file.xml,包含 10^12 个元素的 XML 序列化的实体数组。我需要在序列化文件中添加另外 10^5 个元素,但我不想读取所有以前的元素,追加新元素并将新数组写入流,因为它会占用大量资源(尤其是内存) .

如果它需要二进制序列化器,我不会有任何问题。

【问题讨论】:

  • 这个问题是混搭的。什么是“XML 序列化数组”?只是一个带有根元素的 XML 文件,然后是很多子元素,没有其他层次结构?这个问题完全取决于数据的结构,关于它的细节很少。
  • 使用 IFormatter.Serialize() 序列化的类的数组。实际上,序列化程序的类型并不重要。出于性能原因,我稍后可能会使用二进制文件。

标签: c# optimization memory serialization


【解决方案1】:

一般的解决方案是更改 XML 字节,这样您就不必像反序列化那样读取所有字节。

一般的步骤是:

  1. 列表项
  2. 打开文件流
  3. 存储数组的结束节点
  4. 序列化新项目
  5. 将序列化字节写入流
  6. 写结束节点

例如将整数添加到序列化数组的代码:

// Serialize array - in you case it the stream you read from file.xml
var ints = new[] { 1, 2, 3 };
var arraySerializer = new XmlSerializer(typeof(int[]));
var memoryStream = new MemoryStream(); // File.OpenWrite("file.xml")
arraySerializer.Serialize(new StreamWriter(memoryStream), ints);

// Save the closing node
int sizeOfClosingNode = 13; // In this case: "</ArrayOfInt>".Length
                            // Change the size to fit your array
                            // e.g. ("</ArrayOfOtherType>".Length)

// Set the location just before the closing tag
memoryStream.Position = memoryStream.Length - sizeOfClosingNode;

// Store the closing tag bytes
var buffer = new byte[sizeOfClosingNode];
memoryStream.Read(buffer, 0, sizeOfClosingNode);

// Set back to location just before the closing tag.
// In this location the new item will be written.
memoryStream.Position = memoryStream.Length - sizeOfClosingNode;

// Add to serialized array an item
var itemBuilder = new StringBuilder();
// Write the serialized item as string to itemBuilder
new XmlSerializer(typeof(int)).Serialize(new StringWriter(itemBuilder), 4);
// Get the serialized item XML element (strip the XML document declaration)
XElement newXmlItem = XElement.Parse(itemBuilder.ToString());
// Convert the XML to bytes can be written to the file
byte[] bytes = Encoding.Default.GetBytes(newXmlItem.ToString());
// Write new item to file.
memoryStream.Write(bytes, 0, bytes.Length);
// Write the closing tag.
memoryStream.Write(buffer, 0, sizeOfClosingNode);

// Example that it works
memoryStream.Position = 0;
var modifiedArray = (int[]) arraySerializer.Deserialize(memoryStream);
CollectionAssert.AreEqual(new[] { 1, 2, 3, 4 }, modifiedArray);

【讨论】:

  • 嗨 Elisha,这似乎是一个非常有趣的答案,我已经投了赞成票。对于像我这样在这方面没有太多经验的人来说,如果你可以在你的代码中添加更多的 cmets 将会很有帮助:例如,上面使用的值 #13 来自哪里(它基于你对将 int 数组 { 1,2,3 } 写入内存流需要多少字节?)。另一个问题:您的示例显示了对内存流的修改(您将文件写入为 XML 注释掉):可以用没有主要 mods 的内存流代替吗?谢谢,
  • @BillW,添加了一些 cmets,这不是直观的代码,所以我希望它有所帮助:) 数字 #13 将根据序列化的类型而改变。它表示关闭数组 XML 的节点名称长度。例如, 将占用 16 个字节(每个字符的字节数)。我使用 MemoryStream 只是为了使答案清晰易读(我想我不太成功),但它与 FileStream 共享相同的基础。两者都是流,将现实生活中的第一个块替换为 File.OpenWrite("file.xml") 不会影响负责添加新项目的其余代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-01
  • 1970-01-01
  • 2010-12-16
  • 2023-03-03
  • 2017-07-18
  • 1970-01-01
相关资源
最近更新 更多