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ます。XmlReaderタグにjust を配置して<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

XElement を受け取り、そのノードからデータを読み取る独自の Account オブジェクトを作成できます。次に例を示します。

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 ノードが作成されます。

列挙可能な Charges クラスと LineItem クラスを作成する必要がありますが、必要なものだけを作成します。

次のような XPath ルックアップを使用してアカウントを設定できます。

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

XPath は で見つかりusing System.Xml.XPathます。

于 2012-10-10T18:27:18.463 に答える