Question

Here's an example file:

<?xml version="1.0" encoding="UTF-8"?>
<response status="success">
   <campaigns>
      <campaign>
         <campaign_id>41381</campaign_id>
         <campaign_name><![CDATA[campaign1]]></campaign_name>
         <campaign_status>1</campaign_status>
         <campaign_type>STANDARD</campaign_type>
         <campaign_notes />
         <campaign_rate />
         <campaign_owner_id>33975</campaign_owner_id>
         <campaign_start_date>11-05-2014</campaign_start_date>
         <campaign_end_date>12-12-2020</campaign_end_date>
         <creation_date>11-05-2014</creation_date>
         <daily_budget>10.000</daily_budget>
         <daily_budget_left>10.000000000000000000000000000000</daily_budget_left>
         <total_budget>X</total_budget>
         <total_budget_left>1000000.000000000000000000000000000000</total_budget_left>
         <reporting>
            <impressions />
            <clicks />
            <total_cost>
               <currency>USD</currency>
               <amount />
            </total_cost>
            <average_cpc>
               <currency>USD</currency>
               <amount>0</amount>
            </average_cpc>
            <conversions />
            <cost_per_conversion>
               <currency>USD</currency>
               <amount>n/a</amount>
            </cost_per_conversion>
         </reporting>
      </campaign>
   </campaigns>
</response>

What I want to do is go through each of the campaigns and parse through the data to generate in memory objects. For example I want to create ruby objects based off of each campaign. I'd like to be able to something like campaigns.each {|campaign| puts impressions = campaign['reporting']['impressions']}

Was it helpful?

Solution

Here's some code for your requirement described in your post. It only works for Hash like XML structures, such as the campaign node in your sample data. If you want Array like behavior, you may need to handle them explicitly, as what I did for the campaigns node.

require 'nokogiri'

def parse(element)
  children = element.children.reject{|e| e.is_a?(Nokogiri::XML::Text) && e.text =~ /^\s*$/}

  if children.count == 1 && children[0].is_a?(Nokogiri::XML::Text)
    children[0].text
  else
    data = Hash.new
    children.each do |child|
      data[child.name] = parse(child)
    end
    data
  end
end

doc = Nokogiri::XML(open('data.xml')) # suppose the xml is stored in data.xml

campaigns = doc.xpath('/response/campaigns/campaign').map{|c| parse(c)}
p campaigns
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top