Разбор 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-парсера. Сам файл не кажется таким большим, @190 МБ. Но я сталкиваюсь с ошибками при попытке проанализировать его без парсера 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
Чтобы уточнить, сценарий в настоящее время добавляет идентификатор, первый, последний, средний, пол в виде отдельных записей. можно ли их как-то объединить или изменить способ их добавления? - 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