1

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

想法?

4

3 回答 3

2

我看到的问题是,如果我将所有这些对象存储在内存中,则会导致性能问题,因为它在那些 50k+ 记录文件中运行。

先测试一下。50k * 1kB 仍然只有 50 MB。

不要解决你没有的问题。

于 2012-10-10T17:52:13.287 回答
2

XmlSerializer.Deserialize接受一个XmlReader参数。您可以将XmlReaderjust 放在<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();
    }
}

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

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

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);
    }
}
于 2012-10-11T07:10:37.847 回答
0

您可以创建自己的 Account 对象,这些对象将采用 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")); } }
}

我正在使用这些扩展来获取处理空节点/默认值的信息,例如上面,0 是 Number get 的默认 int 值。GetElement()如果不存在,则创建一个新的 Charges 节点。

您将需要创建可枚举的 Charges 类和 LineItem 类,但您只需根据需要创建所需的内容。

您可以使用 XPath 查找来填充帐户,例如:

Account account = new Account(
    root.XPathSelectElement("Account[acctnum='"+ someAccount + "']"));

XPath 与using System.Xml.XPath.

于 2012-10-10T18:27:18.463 回答