在C#中即时序列化大量对象而不是一次性?

时间:2012-10-10 17:49:10

标签: c# xml oop serialization

我创建了几个类来表示关系数据结构(父子结构)。下面是一个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语句更容易。

思想?

3 个答案:

答案 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();
    }
}

您还可以创建一个实现IXmlSerializableBillingFile类,并将此功能放在那里。

或者如果您更喜欢基于推送的模型:

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。