1

I am new to ruby and rails programming and I need to parse an xml file that I get as a response and store the station names in an array. A sample of the xml is as follows :

<Stations>
<Station>
<Code>HT</Code>
<Type>knooppuntIntercitystation</Type>
<Namen>
<Kort>Den Bosch</Kort>
<Middel>'s-Hertogenbosch</Middel>
<Lang>'s-Hertogenbosch</Lang>
</Namen>
<Land>NL</Land>
<UICCode>8400319</UICCode>
<Lat>51.69048</Lat>
<Lon>5.29362</Lon>
<Synoniemen>
<Synoniem>Hertogenbosch ('s)</Synoniem>
<Synoniem>Den Bosch</Synoniem>
</Synoniemen>
</Station>
<Station>
<Code>HTO</Code>
<Type>stoptreinstation</Type>
<Namen>
<Kort>Dn Bosch O</Kort>
<Middel>Hertogenbosch O.</Middel>
<Lang>'s-Hertogenbosch Oost</Lang>
</Namen>
<Land>NL</Land>
<UICCode>8400320</UICCode>
<Lat>51.700553894043</Lat>
<Lon>5.3183331489563</Lon>
<Synoniemen>
<Synoniem>Hertogenbosch Oost ('s)</Synoniem>
<Synoniem>Den Bosch Oost</Synoniem>
</Synoniemen>
</Station>
</Stations>

I need to get the Code and the Lang name in an array of hashes or just the lang name in an array.

How can I do that in ruby ? thanks in advance

Eric Duminil
  • 52,989
  • 9
  • 71
  • 124
  • I'd recommend searching for "ruby xml parser" and "parsing xml with ruby" plus variations on those. Please read "[ask]" along with the linked pages. – the Tin Man Nov 16 '16 at 17:24

2 Answers2

2

you can use

hash = Hash.from_xml(xml)

Refrence doc:

http://apidock.com/rails/v4.0.2/Hash/from_xml/class

kajal ojha
  • 1,248
  • 8
  • 20
1

Here's a solution which doesn't require Rails but a small gem (xml-simple) :

#  gem install xml-simple
require 'xmlsimple'

stations = XmlSimple.xml_in(xml, :ForceArray => ['Station', 'Synoniem'])

codes_and_langs = stations['Station'].map{|station| {:code => station["Code"], :lang => station.fetch("Namen",{})["Lang"]}}
puts codes_and_langs.inspect
#=> [{:code=>"HT", :lang=>"'s-Hertogenbosch"}, {:code=>"HTO", :lang=>"'s-Hertogenbosch Oost"}]

If you are using Rails or have Rails installed :

require 'active_support/core_ext/hash' # <- Use this line for non-Rails Ruby scripts.

hash            = Hash.from_xml(xml)
root_node       = hash["Stations"]     || {}
stations        = root_node["Station"] || []
codes_and_langs = stations.compact.map do |station|
  {
    :code       => station["Code"],
    :lang       => station.fetch('Namen',{})['Lang']
  }
end
puts codes_and_langs.inspect
#[{:code=>"HT", :lang=>"'s-Hertogenbosch"}, {:code=>"HTO", :lang=>"'s-Hertogenbosch Oost"}]

just_langs = stations.compact.map do |station|
  station.fetch('Namen',{})['Lang']
end

puts just_langs.inspect
# ["'s-Hertogenbosch", "'s-Hertogenbosch Oost"]

Hash#fetch is used to avoid an exception if "Namen" isn't defined.

Here's xml variable for both scripts :

xml="<Stations>
    <Station>
    <Code>HT</Code>
    <Type>knooppuntIntercitystation</Type>
    <Namen>
    <Kort>Den Bosch</Kort>
    <Middel>'s-Hertogenbosch</Middel>
    <Lang>'s-Hertogenbosch</Lang>
    </Namen>
    <Land>NL</Land>
    <UICCode>8400319</UICCode>
    <Lat>51.69048</Lat>
    <Lon>5.29362</Lon>
    <Synoniemen>
    <Synoniem>Hertogenbosch ('s)</Synoniem>
    <Synoniem>Den Bosch</Synoniem>
    </Synoniemen>
    </Station>
    <Station>
    <Code>HTO</Code>
    <Type>stoptreinstation</Type>
    <Namen>
    <Kort>Dn Bosch O</Kort>
    <Middel>Hertogenbosch O.</Middel>
    <Lang>'s-Hertogenbosch Oost</Lang>
    </Namen>
    <Land>NL</Land>
    <UICCode>8400320</UICCode>
    <Lat>51.700553894043</Lat>
    <Lon>5.3183331489563</Lon>
    <Synoniemen>
    <Synoniem>Hertogenbosch Oost ('s)</Synoniem>
    <Synoniem>Den Bosch Oost</Synoniem>
    </Synoniemen>
    </Station>
    </Stations>
    "
Eric Duminil
  • 52,989
  • 9
  • 71
  • 124
  • I have rails, so I was using your second suggestion but I get the following error "NoMethodError Exception: undefined method `[]' for nil:NilClass" . Sorry if I am sounding stupid but I am really new to this – John Avlakiotis Nov 16 '16 at 13:21
  • The code works fine with the example you posted, maybe your bigger xml isn't formatted the same or has missing nodes. I'll update the code with a more robust solution. – Eric Duminil Nov 16 '16 at 13:26
  • The updated version should work with empty Station nodes. – Eric Duminil Nov 16 '16 at 13:35
  • Thanks! THis works. And something simpler, how about if I wanted instead of a hash just to make an array with the Long names? – John Avlakiotis Nov 16 '16 at 14:43
  • thanks that works as well, and a final question if you can help me, how would i print the just_langs in my index.html.erb, since I am using rails and it is part of what I want to do. – John Avlakiotis Nov 16 '16 at 15:04
  • <% @just_langs.each do |t| %> <%= t %>
    <% end %>
    – John Avlakiotis Nov 16 '16 at 15:11
  • This should be another question. It has been answered on stackoverflow already, and if you initialize @just_langs in the corresponding action inside your controller, the above code should work. – Eric Duminil Nov 16 '16 at 15:16
  • Eric it was working ok in my console but in rails it doesnt work, everything after the hash variable is empty. I place a debugger and it breaks, and the error message I get is "no implicit conversion of String into Integer" for "root_node = hash["Stations"] || {}" Any idea why this is happening ? I believe that I need to put this in my configuration, "require 'active_support/core_ext/hash' ". Any idea? Thanks again for all your trouble and time – John Avlakiotis Nov 16 '16 at 16:50
  • actually when I use @hash.kind_of?(Hash) or (Array) it returns false – John Avlakiotis Nov 16 '16 at 17:09
  • You don't need any require if you use Rails. What does hash.class return? What does hash.inspect look like? At least the beginning if it's too long? – Eric Duminil Nov 16 '16 at 20:12
  • @hash.inspect returns the following (the beginning) : ""{\"stations\"=>{\"station\"=>[{\"name\"=>\"'s-Gravenhage\", \"code\"=>\"GVC\", \"country\"=>\"NL\", \"lat\"=>\"52.0802764892578\", \"long\"=>\"4.32499980926514\", \"alias\"=>\"true\"}, {\"name\"=>\"'s-Hertogenbosch\", \"code\"=>\"HT\", \"country\"=>\"NL\", \"lat\"=>\"51.69048\", \"long\"=>\"5.29362\", \"alias\"=>\"false\"}, {\"name\"=>\"'s-Hertogenbosch Oost\", \"code\"=>\"HTO\", \"country\"=>\"NL\", \"lat\"=>\"51.700553894043\", \"long\"=>\ " . It looks that the hash is alright now, but "@root_node","@stations" are empty hashes – John Avlakiotis Nov 17 '16 at 08:29
  • All your keys are lower case. You need to adapt the code : `Stations->stations` and so on – Eric Duminil Nov 17 '16 at 08:31
  • controller : class ClientController < ApplicationController require 'active_support/core_ext/hash' def index ns_client = Ns::Client.new('username', password') available_stations = ns_client.get_stations hash = Hash.from_xml(available_stations) root_node = hash["Stations"] || {} stations = root_node["Station"] || [] debugger codes_and_langs = stations.compact.map do |station| { :code => station["Code"], :lang => station.fetch('Namen',{})['Lang'] } end end end – John Avlakiotis Nov 17 '16 at 08:33
  • You're welcome. Please accept the answer if you're happy with it. – Eric Duminil Nov 17 '16 at 12:22