Разбор на XML файл с анализатора на Nokogiri SAX

Прочетох малко и се опитвах да получа определени данни от голям XML файл. Данните изглеждат така:

<Provider ID="0042100323">
    <Last_Name>LastName</Last_Name>
    <First_Name>FirstName</First_Name>
    <Mdl_Name>Middle</Mdl_Name>
    <Gndr>M</Gndr>
</Provider>

Бих искал да напиша start_element, за да добавя всичко това към масив със записи като:

0042100323, LastName, FirstName, Middle, M

използвайки нещо като:

def start_element name, attributes = []
    @@records << attributes[0][1] if name == "Provider"
end

Как мога да актуализирам кода си, за да добавя другите тагове към масива?


person Todd J.    schedule 10.11.2014    source източник
comment
Колко голям е XML файлът? Това, което свикнахме да смятаме за голямо и оправдано с помощта на SAX анализатор, сега е със среден размер и може лесно да се обработва с помощта на DOM анализатор.   -  person the Tin Man    schedule 10.11.2014
comment
Опитах се да го стартирам с помощта на DOM-парсера. Самият файл не изглежда толкова голям @190MB. Но се натъквам на грешки, когато се опитвам да го анализирам без SAX анализатора.   -  person Todd J.    schedule 10.11.2014


Отговори (3)


Използвайте characters събитие, за да получите текст вътре таг:

def characters(string)
  @records << string
end

АКТУАЛИЗАЦИЯ според коментара на OP:

за избиране на текст според съдържащия таг; запомнете последния видян етикет и вземете текст само ако последният видян етикет е това, което искате.

def start_element(name, attributes = [])
  @records << attributes[0][1] if name == "Provider"
  @last_seen_tag = name
end

def characters(string)
  if ['Last_Name', 'First_Name', 'Mdl_Name', 'Gndr'].include? @last_seen_tag
    @records << string
  end
end

def end_element name
  @last_seen_tag = nil
end
person falsetru    schedule 10.11.2014
comment
Има ли начин да взема само етикетите, от които се нуждая? Данните, които дадох, бяха данните, от които имах нужда, но самият XML има много повече полета от това, което изброих. Добавих символното събитие и то започна да хваща низовете във всеки таг в XML файла. - person Todd J.; 10.11.2014
comment
@ToddJ., актуализирах отговора според вашия коментар. Също така актуализирах кода, за да използвам променлива на екземпляр вместо променлива на клас. - person falsetru; 10.11.2014
comment
това ми помогна да постигна голям напредък в моя проект. много благодаря! - person Todd J.; 10.11.2014
comment
За да изясним обаче, скриптът в момента добавя id, първи, последен, среден, пол като отделни записи. мога ли да ги комбинирам по някакъв начин или да променя метода на добавянето им? - person Todd J.; 10.11.2014
comment
@ToddJ., не съм сигурен, че те разбрах правилно. Искате ли нещо подобно? pastebin.com/Nquy9VrP - person falsetru; 11.11.2014
comment
Да точно :) благодаря ви за помощта! разбирам, че ред 2 създава запис в масив, след това ред 8 добавя всичко, посочено в ред 7 към масива, след това ред 14 добавя всичко дотук в друг масив като единичен запис? (работи, просто се опитвам да науча защо) - person Todd J.; 13.11.2014
comment
@ToddJ., @fields е временен масив. Първо се създава, виждайки етикет Provider, текстовете се добавят и когато етикетът Provider приключи, временният масив се добавя към резултата @record. - person falsetru; 13.11.2014

Можете да използвате следния подход: -

require 'nokogiri'

@doc = Nokogiri::XML.parse <<-EOT
<Provider ID="0042100323">
    <Last_Name>LastName</Last_Name>
    <First_Name>FirstName</First_Name>
    <Mdl_Name>Middle</Mdl_Name>
    <Gndr>M</Gndr>
</Provider>
EOT

def start_element name, attributes = [], childs = []
  node = @doc.at("//*[local-name()='#{name}']")
  contents_of_childs = node.search("./child::*").each_with_object([]) do |n, a|
    a << n.text if childs.include?(n.name)
  end
  attributes.each_with_object([]) do |attr, a|
    a << node[attr] unless node[attr].nil?
  end + contents_of_childs
end

start_element('Provider', ['ID'], %w(Last_Name First_Name Mdl_Name Gndr))
# => ["0042100323", "LastName", "FirstName", "Middle", "M"]
person Arup Rakshit    schedule 10.11.2014

Вариация на отговора на Arups:

require 'nokogiri'

@doc = Nokogiri::XML.parse <<-EOT
<Provider ID="0042100323">
    <Last_Name>LastName</Last_Name>
    <First_Name>FirstName</First_Name>
    <Mdl_Name>Middle</Mdl_Name>
    <Gndr>M</Gndr>
</Provider>
EOT

(@doc.at("//Provider")["ID"] + @doc.text).split 
 #=> ["0042100323", "LastName", "FirstName", "Middle", "M"]
person Bala    schedule 10.11.2014