0

TeleForm というアプリケーションからの XML を処理しようとしています。これはフォーム スキャン ソフトウェアで、データを取得して XML に変換します。これは XML のスニペットです

<?xml version="1.0" encoding="ISO-8859-1"?>
<Records>
  <Record>
    <Field id="ImageFilename" type="string" length="14"><Value>00000022000000</Value></Field>
    <Field id="Criterion_1" type="number" length="2"><Value>3</Value></Field>
    <Field id="Withdrew" type="string" length="1"></Field>
  </Record>

  <Record>
    <Field id="ImageFilename" type="string" length="14"><Value>00000022000001</Value></Field>
    <Field id="Criterion_1" type="number" length="2"><Value>3</Value></Field>
    <Field id="Withdrew" type="string" length="1"></Field>
  </Record>
</Records>

おそらく私たちが書いたカスタムパーサーを使用して、他のシステムでこれを処理しました。Railsでは問題ないと思っていたのですが、違いました。

これを Hash.from_xml または Nokogiri で解析しても、期待した結果が得られません。

{"Records"=>{"Record"=>[{"Field"=>["", {"id"=>"Criterion_1", "type"=>"number", "length"=>"2", "Value"=>"3"}, ""]},
 {"Field"=>["", {"id"=>"Criterion_1", "type"=>"number", "length"=>"2", "Value"=>"3"}, ""]}]}}

これにあまりにも多くの時間を費やした後、タイプと長さの属性を gsub アウトすると、期待どおりの結果が得られることがわかりました (たとえそれが間違っていたとしても! 最初のレコード ノードだけを削除しました)。

{"Records"=>{"Record"=>[{"Field"=>[{"id"=>"ImageFilename", "Value"=>"00000022000000"}, 
{"id"=>"Criterion_1", "type"=>"number", "length"=>"2", "Value"=>"3"}, {"id"=>"Withdrew"}]}, 
{"Field"=>["", {"id"=>"Criterion_1", "type"=>"number", "length"=>"2", "Value"=>"3"}, ""]}]}}

XML に精通していないので、type 属性と length 属性を使用するこのスタイルの XML は、データ型に変換しようとしていると思います。その場合、"Withdrew" 属性が空として表示された理由は理解できますが、"ImageFilename" が空である理由がわかりません。これは 14 文字の文字列です。

gsub で回避できましたが、これは無効な XML ですか? DTD (TeleForm が提供するはずだった) を追加すると、異なる結果が得られますか?

編集

編集としていくつかのコードを使用して、私自身の質問に対する可能な回答を提供します。このコードは、Mark Thomas から受け取った 1 つの回答の機能の一部に従っていますが、次の理由で Nokogiri を使用しないことにしました。

  • xml は一貫しており、常に同じタグ (/Records/Record/Field) と属性が含まれています。
  • 各 XML ファイルには数百のレコードが存在する可能性があり、Nokogiri はわずか 26 レコードで少し遅いようです
  • Hash.from_xml を取得して期待どおりの結果を得る方法を見つけました (type="string" は好きではありませんが、ハッシュを使用してクラスにデータを入力するだけです。

1 つの完全なレコードを含む XML の拡張バージョン

<?xml version="1.0" encoding="ISO-8859-1"?>
<Records>
  <Record>
    <Field id="ImageFilename" type="string" length="14"><Value>00000022000000</Value></Field>
    <Field id="DocID" type="string" length="15"><Value>731192AIINSC</Value></Field>
    <Field id="FormID" type="string" length="6"><Value>AIINSC</Value></Field>
    <Field id="Availability" type="string" length="18"><Value>M  T  W  H  F  S</Value></Field>
    <Field id="Criterion_1" type="number" length="2"><Value>3</Value></Field>
    <Field id="Criterion_2" type="number" length="2"><Value>3</Value></Field>
    <Field id="Criterion_3" type="number" length="2"><Value>3</Value></Field>
    <Field id="Criterion_4" type="number" length="2"><Value>3</Value></Field>
    <Field id="Criterion_5" type="number" length="2"><Value>3</Value></Field>
    <Field id="Criterion_6" type="number" length="2"><Value>3</Value></Field>
    <Field id="Criterion_7" type="number" length="2"><Value>3</Value></Field>
    <Field id="Criterion_8" type="number" length="2"><Value>3</Value></Field>
    <Field id="Criterion_9" type="number" length="2"><Value>3</Value></Field>
    <Field id="Criterion_10" type="number" length="2"><Value>3</Value></Field>
    <Field id="Criterion_11" type="number" length="2"><Value>0</Value></Field>
    <Field id="Criterion_12" type="number" length="2"><Value>0</Value></Field>
    <Field id="Criterion_13" type="number" length="2"><Value>0</Value></Field>
    <Field id="Criterion_14" type="number" length="2"><Value>0</Value></Field>
    <Field id="Criterion_15" type="number" length="2"><Value>0</Value></Field>
    <Field id="DayTraining" type="string" length="1"><Value>Y</Value></Field>
    <Field id="SaturdayTraining" type="string" length="1"></Field>
    <Field id="CitizenStageID" type="string" length="12"><Value>731192</Value></Field>
    <Field id="NoShow" type="string" length="1"></Field>
    <Field id="NightTraining" type="string" length="1"></Field>
    <Field id="Withdrew" type="string" length="1"></Field>
    <Field id="JobStageID" type="string" length="12"><Value>2292</Value></Field>
    <Field id="DirectHire" type="string" length="1"></Field>
  </Record>
</Records>

私は、4D と Active4D で書かれた古いシステムを置き換えるワークフローのプロトタイプを試しているだけです。TeleForms データを処理するこの領域は、バッチ操作として実装されており、まだ元に戻る可能性があります。新しい Rails の実装で、古い実行可能な概念のいくつかをマージしようとしています。XML ファイルは共有サーバー上にあり、おそらく Web ルートに移動してから、ファイルを処理するようにトリガーを設定する必要があります。

私はまだ定義段階にありますが、InterviewForm を処理するためのモジュール/クラスは次のようになり、変更される可能性があります (エラー トラップはほとんどなく、まだテストに入ろうとしており、私の Ruby は、遊んだ後は本来あるべきほど良くありませんRails で約 5 年間!):

module Teleform::InterviewForm

  class Form < Prawn::Document
    # Not relevant to this question, but this class generates the forms from a Fillable PDF template and 
    # relavant Model(s) data.
    # These forms, when completed are what is processsed by TeleForms and produces the xml.
  end

  class RateForms
    attr_accessor  :records, :results

    def initialize(xml_path)
      fields = []
      xml = File.read(xml_path)
      # Hash.from_xml does not like a type of "string"
      hash = Hash.from_xml(xml.gsub(/type="string"/,'type="text"'))
      hash["Records"]["Record"].each do |record|
        #extract the field form each record
        fields << record["Field"]
      end
      @records = []
      fields.each do |field|
        #build the records for the form
        @records << Record.new(field)
      end
      @results = rate_records
    end

    def rate_records
      # not relevant to the qustions but this is where the data is processed and a bunch of stuff takes place
      return "Any errors"
    end
  end


  class Record
    attr_accessor(*[:image_filename, :doc_id, :form_id, :availability, :criterion_1, :criterion_2, 
      :criterion_3, :criterion_4, :criterion_5, :criterion_6, :criterion_7, :criterion_8, 
      :criterion_9, :criterion_10, :criterion_11, :criterion_12, :criterion_13, :criterion_14, :criterion_15, 
      :day_training, :saturday_training, :citizen_stage_id, :no_show, :night_training, :withdrew, :job_stage_id, :direct_hire])

    def initialize(fields)
      fields.each do |field|
        if field["type"] == "number"
          try("#{field["id"].underscore.to_sym}=", field["Value"].to_i)
        else
          try("#{field["id"].underscore.to_sym}=", field["Value"])
        end
      end
    end
  end

end
4

2 に答える 2

0

これがインタビュー対象者の評価であるという追加情報を追加していただきありがとうございます。コードでこのドメイン情報を使用すると、改善される可能性があります。コードを投稿していませんが、通常、ドメイン オブジェクトを使用すると、コードがより簡潔で読みやすくなります。Ratingデータを XML からデータ構造に変換するのではなく、 を表す単純なクラスを作成することをお勧めします。

class Rating
  attr_accessor :image_filename, :criterion_1, :withdrew
end

上記のクラスを使用して、Nokogiri を使用して XML からフィールドを抽出する 1 つの方法を次に示します。

doc = Nokogiri::XML(xml)
ratings = []

doc.xpath('//Record').each do |record|
    rating = Rating.new
    rating.image_filename = record.at('Field[@id="ImageFilename"]/Value/text()').to_s
    rating.criterion_1 = record.at('Field[@id="Criterion_1"]/Value/text()').to_s
    rating.withdrew = record.at('Field[@id="Withdrew"]/Value/text()').to_s
    ratings << rating
end

現在、オブジェクトratingsのリストであり、それぞれにデータを取得するメソッドがあります。Ratingこれは、深いデータ構造を掘り下げるよりもはるかにクリーンです。たとえば、true または false を返すメソッドをRating作成するなど、クラスをさらに改善することもできます。withdrew?

于 2013-02-17T01:17:01.640 に答える