2

Builderを使用して、ActiveRecordモデルのテーブルに基づいて一連のXMLファイルを作成したいと思います。私は100万行近くあるのでfind_each(batch_size: 5000)、レコードがなくなるまで、レコードを反復処理し、それらのバッチごとにXMLファイルを書き込むために使用する必要があります。次のようなもの:

filecount = 1
count = 0
xml = ""
Person.find_each(batch_size: 5000) do |person|
  xml += person.to_xml  # pretend .to_xml() exists 
  count += 1       

  if count == MAX_PER_FILE
    File.open("#{filecount}.xml", 'w') {|f| f.write(xml) }
    xml = ""
    filecount += 1
    count = 0
  end
end

これは、次のようにブロックで機能するため、Builderのインターフェイスではうまく機能しません。

xml = builder.person { |p| p.name("Jim") }

ブロックが終了すると、Builderは現在のスタンザを閉じます。pへの参照を保持して、ブロックの外で使用することはできません(私は試しました)。基本的に、Builderはイテレーションを「所有」したいと考えています。

したがって、ビルダーでこれを機能させるには、次のようなことを行う必要があります。

  filecount = 0
  offset = 0
  while offset < Person.count do
    count = 0
    builder = Builder::XmlMarkup.new(indent: 5)
    xml = builder.people do |people|
      Person.limit(MAX_PER_FILE).offset(offset).each do |person|
        people.person { |p| p.name(person.name) }
        count += 1
      end
    end

    File.open("#output@file_count.xml", 'w') {|f| f.write(xml) }
    filecount += 1
    offset += count
  end

ブロック構文なしでBuilderを使用する方法はありますか?ブロックに依存するのではなく、プログラムで「現在のスタンザを閉じる」ように指示する方法はありますか?

4

2 に答える 2

1

Larry Kの提案に従って、XMLを手動で生成することになりました。Rubyの組み込みXMLエンコーディングにより、これは簡単なものになりました。なぜこの機能がもっと広く宣伝されていないのかわかりません...to_xs組み込みに出くわす前に、グーグルでさまざまな実装を試してみるのに多くの時間を無駄にしました"foo".encode(xml: :text)

私のコードは次のようになります:

  def run
    count = 0
    Person.find_each(batch_size: 5000) do |person|
      open_new_file if @current_file.nil?

      # simplified- I actually have many more fields and elements
      #
      @current_file.puts "     <person>#{person.name.encode(xml: :text)}</person>"

      count += 1

      if count == MAX_PER_FILE
        close_current_file
        count = 0
      end
    end

    close_current_file
  end

  def open_new_file
    @file_count += 1
    @current_file = File.open("people#{@file_count}.xml", 'w')
    @current_file.puts "<?xml version='1.0' encoding='UTF-8'?>"
    @current_file.puts "     <people>"
  end

  def close_current_file
    unless @current_file.nil?
      @current_file.puts "     </people>"
      @current_file.close
      @current_file = nil
    end
  end
于 2012-06-25T21:14:32.393 に答える
1

私の提案:ビルダーを使用しないでください。

xmlエンティティを正しくエスケープする限り、XMLは単純な形式です。

db取得をバッチ処理してから、バッチをxmlとしてファイルハンドルに書き出すだけです。例が示すように、文字列を介してバッファリングしないでください。ファイルハンドルに書き込むだけです。OSにバッファリングを処理させます。ファイルのサイズは任意ですが、なぜ制限があるのですか?

また、100万行のインデントスペースは含めないでください。

追加 xmlファイルを作成するときに、ファイルの先頭にxmlコメントも含めます。

  • xmlファイルを生成したソフトウェアの名前とバージョン
  • ファイルが書き込まれた日付/タイムスタンプ
  • その他の役立つ情報。たとえば、この場合、ファイルは元のデータセットのバッチ#xであると言えます。
于 2012-06-26T02:20:03.637 に答える