我创建了几个类来表示关系数据结构(父子结构)。下面是一个XML表示的例子到目前为止让你知道我的意思
<BillingFile>
<Account>
<acctnum>122344231414</acctnum>
<adjustments>34.44</adjustments>
<Charges>
<lineitem>
<chargetype>PENALTY</chargetype>
<amount>40.50</amount>
<ratecode>E101</ratecode>
</lineitem>
<lineitem>
<chargetype>LATE CHARGE</chargetype>
<amount>445.35</amount>
<ratecode>D101</ratecode>
</lineitem>
</Charges>
</Account>
</BillingFile>
我正在使用我的应用程序正在解析一个大文本文件,该文件可能包含超过50,000个帐户。每次读取帐户时,我都会创建一个具有父对象等的“帐户”对象。最终目标是能够创建一个XML文件,其中包含从创建的对象序列化的所有帐户信息。
我看到的问题是,如果我将所有这些对象存储在内存中,它将导致性能问题,因为它在那些50k +记录文件中运行。
我想知道的是,有没有办法在C#中顺序序列化一个对象,而不是一次全部?
我已经完成了一些谷歌搜索,似乎.NET的内置序列化方法是一个完成的交易。有没有更好的方法可以做到这一点?
我宁愿避免做任何中间步骤,例如将数据存储在数据库中,因为修改代码比处理一堆表和JOIN语句更容易。
思想?
答案 0 :(得分:2)
我看到的问题是,如果我将所有这些对象存储在内存中,它将导致性能问题,因为它在那些50k +记录文件中运行。
先测试一下。 50k * 1kB仍然只有50 MB。
不要解决你没有的问题。
答案 1 :(得分:2)
XmlSerializer.Deserialize
需要XmlReader
个参数。您可以将XmlReader
放在<Account>
标记处,然后在那里调用XmlSerializer
。
public IEnumerable<Account> ReadAccounts(TextReader source)
{
var ser = new XmlSerializer(typeof(Account));
using (var reader = XmlReader.Create(source))
{
if (!reader.IsStartElement("BillingFile"))
{
yield break;
}
reader.Read();
while (reader.MoveToContent() == XmlNodeType.Element)
{
yield return (Account) ser.Deserialize(reader);
}
}
}
类似于序列化
public void WriteAccounts(IEnumerable<Account> data, TextWriter target)
{
// Use XmlSerializerNamespaces to supress xmlns:xsi and xmlns:xsd
var namespaces = new XmlSerializerNamespaces();
namespaces.Add("", "");
var ser = new XmlSerializer(typeof(Account));
using (var writer = XmlWriter.Create(target))
{
writer.WriteStartElement("BillingFile");
foreach (var acct in data)
{
ser.Serialize(writer, acct, namespaces);
writer.Flush();
}
writer.WriteEndElement();
}
}
您还可以创建一个实现IXmlSerializable
的BillingFile
类,并将此功能放在那里。
或者如果您更喜欢基于推送的模型:
public class AccountWriter : IDisposable
{
private XmlWriter _writer;
private XmlSerializer _ser;
private XmlSerializerNamespaces _namespaces;
private bool _wroteHeader = false;
private bool _disposed = false;
public bool IsDisposed { get { return _disposed; } }
public AccountWriter(TextWriter target)
{
_namespaces = new XmlSerializerNamespaces();
_namespaces.Add("", "");
_ser = new XmlSerializer(typeof(Account));
_writer = XmlWriter.Create(target);
}
public void Write(Account acct)
{
if (_disposed) throw new ObjectDisposedException("AccountWriter");
if (!_wroteHeader)
{
_writer.WriteStartElement("BillingFile");
_wroteHeader = true;
}
_ser.Serialize(_writer, acct, _namespaces);
}
public void Flush()
{
if (_disposed) throw new ObjectDisposedException("AccountWriter");
_writer.Flush();
}
public void Dispose()
{
if (!_disposed)
{
if (_wroteHeader)
{
_writer.WriteEndElement();
_wroteHeader = true;
}
_writer.Dispose();
_disposed = true;
}
}
}
using (var writer = new AccountWriter(Console.Out))
{
foreach (var acct in accounts)
{
writer.Write(acct);
}
}
答案 2 :(得分:0)
您可以创建自己的帐户对象,这些对象将接受XElement并从该节点读取数据,例如:
public class Account
{
XElement self;
public Account(XElement account)
{
if(null == account)
self = new XElement("Account");
else
self = account;
}
public int Number
{
get { return self.Get("acctnum", 0); }
set { self.Set("acctnum", value, false); }
}
public Charges Charges { get { return new Charges(self.GetElement("Charges")); } }
}
我正在使用these extensions来获取处理空节点/默认值的信息,如上所述,0是Number get的默认int值。 GetElement()
如果不存在,则会创建一个新的Charges节点。
您需要创建可枚举的费用类&amp; LineItem类,但您只需根据需要创建所需的内容。
您可以使用XPath查找填充帐户,如:
Account account = new Account(
root.XPathSelectElement("Account[acctnum='"+ someAccount + "']"));
使用using System.Xml.XPath
找到XPath。