Ruby – práce se soubory (xml a json)

Minule jsme se podívali na formáty txt a csv. Nyní si ukážeme základní zpracování dat ve formátu json a xml.

1 – JSON. Zde si vezmu na výpomoc příklad použitý na www.tutorialspoint.com (https://www.tutorialspoint.com/json/json_ruby_example.htm)

Mějme následující vstupní soubor, který chceme načíst

{
   "President": "Alan Isaac",
   "CEO": "David Richardson",
  
   "India": [
      "Sachin Tendulkar",
      "Virender Sehwag",
      "Gautam Gambhir"
   ],

   "Srilanka": [
      "Lasith Malinga",
      "Angelo Mathews",
      "Kumar Sangakkara"
   ],

   "England": [
      "Alastair Cook",
      "Jonathan Trott",
      "Kevin Pietersen"
   ]
	
}

V rámci ruby si musíme opět nejdříve vyzádat potřebné knihovny

   require 'json'

a pak již můžeme načíst soubor

   json = File.read('input.json')
   obj = JSON.parse(json)

pro parsování stringu do formátu JSON pak použijeme příkaz

   JSON.parse(string)

velmi důležitá metoda pak je „generate(obj, pots=nil)“ . Tato umožňuje vytvoření JSON dokumentu z Ruby. Na druhé straně pak máme metodu „load(source, proc = nil, options = {})“, která umožňuje získání ruby datové struktury přímo z JSONu.

nyní se podíváme na jednoduchou ukázku práce s formátem JSON. Jedná se jednoduché cvičení ze script Unicorn College

   require "json"
   data = [1, "hello", true, {3 => 4, "key" => "value"}]

   serialized_data = JSON.generate(data)
   puts serialized_data

   data = JSON.parse(serialized_data)

   File.open("data.json", "w") do |file|
     file.write(serialized_data)
   end

     pretty_serialiazed_data = JSON.pretty_generate(data)
     File.open("pretty_data.json", "w") do |file|
     file.write(pretty_serialiazed_data)
   end


   deserialized_data = nil
   File.open("data.json", "r") do |file|
     deserialized_data = JSON.parse(file.read)
   end

2 – XML. Další docela obsáhlou podkapitolou bude práce s xml. Pro zjednodušení a také z praktických důvodů si zde ukážeme pouze zpracování ve spolupráci s knihovnou nokogiri

Opět si musíme nejdříve vyžádat potřičnou knihovnu

   require "nokogiri"

Než se ale pustíme dál, tak se podíváme na dvě motody, které následně použijeme. Metoda xpath vrací kolekci, kdežto metoda at_xpath nám vrátí první položku takovéto kolekce.

Nyní již pojďme na vlastní načítání, zde využiji opět ukázku ze skript Unicorn College

Mějme xml dokument s následující strukturou


  
    James
    Smith
    
    
      
      
      
      
      
    
  

ten pak následně načítáme tímto způsobem

students = []
File.open("study_results.xml", "r") do |file|
  doc = Nokogiri::XML::Document.parse(file)
  doc.root.xpath("student").each do |student_element|
    first_name = student_element.at_xpath("first_name").content
    last_name = student_element.at_xpath("last_name").content
    date_of_birth_element = student_element.at_xpath("date_of_birth")
    day = date_of_birth_element["day"].to_i
    month = date_of_birth_element["month"].to_i
    year = date_of_birth_element["year"].to_i
    student = Student.new(first_name, last_name, Time.new(year, month, day))
    study_results = {}
    student_element.at_xpath("study_results").xpath("subject").each do |subject_element|
      subject_code = subject_element["code"]
      grade = subject_element["grade"]
      study_results[subject_code] = grade
    end
    student.study_results = study_results
    students << student
  end
end

Jak je vidět i samotný code odráží komplexnost dokumentu. Nejinak je tomu u zápisu, kde se opět podíváme na scripta z Unicorn College, která nám přiblíží zápis s pomocí třídy Nokogiri::XML:Builder.

require "nokogiri"
require "yaml"
 
require_relative "quiz_classes.rb"
 
source_file = "quiz_object.yaml"
target_file = "quiz.xml"
 
# load quiz data
quiz = YAML.load_file(source_file)
 
# create a new Builder instance
builder = Nokogiri::XML::Builder.new do |xml|
  # create the root element ("quiz")
  xml.quiz({"name" => quiz.name, "author" => quiz.author}) do
    # iterate over all questions
    quiz.questions.each do |question|
      # create a "question" element
      xml.question({"text" => question.text}) do
        # iterate over all answers
        question.answers.each do |answer|
          # create an "answer" element
          xml.answer(answer.text, {"correct" => answer.correct ? 1 : 0})
        end
      end
    end
  end
end
 
# save the XML document to a file
File.open(target_file, "w") do |file|
  file.puts builder.to_xml
end

Výsledný výstup pak vypadá následovně



  
    Torino
    San Marino
    Vatikan
    Terst
  

Ruby – práce se soubory (txt a csv)

Začneme tím nejjednoduším a postupně budeme přidávat

Než začneme s čtením zkontrolujeme si, zda daný soubor opravdu existuje a v případě, že nikoliv, tak program ukončíme

  
    unless File.exist?("jmeno souboru")
      puts "Nemam soubor"
      exit
    end

1 – Načtení a zápis do .txt

Načtení je v případě .txt super jednoduché, stejně tak zápis. Níže můžete vidět code pro čtení. Nejdříve načteme soubor jako takový a následně zprácováváme jednotlivé řádky. Ukázka zahrnuje i použití příkazu „.strip“, který oseká text o bílé znaky a následně příkaz „.split(“)“ rozseká získaný řetězec na jednotlivé chary a ty následně pomocí příkazu << pošle do pole.

      File.open("jmeno souboru.txt", "r") do |soubor|
        soubor.each_line do |radek|
          radek = radek.strip
          GLOB_POLE << radek.split('')
        end
      end

2 – Zápis používá stejnou logiku jako čtení. Liší se pouze zámněně „r“ za „w“ a tím, že již nenačítáme soubor jako takový.
Robot je jeméno třídy, v jejímž rámci voláme třídní proměnnou roboti. Roboti představují pole instancí třídy Robot, skrz které iterujeme a do každého řádku vypisujeme základní infomrace, které získáváme z jednotlivých instancí

   File.open("jmeno_souboru.txt", "w") do |line|
      Robot.roboti.each do |robot|
        line.puts "#{robot.jmeno}(#{robot.pozice[0]}x#{robot.pozice[1]}):#{robot.stav}"
      end
    end

3 – V zásadě stejnou konstukci použijeme pro práci s .csv soubory. I tady rozlišujeme mezi operací číst – písmeno r – a operací psát – písmeno w. A co je důležité, musíme si vyžádat knihovnu CSV a to náledujícím způsobem

   require "csv"

Načtení pak provedeme následujícím způsobem

    CSV.open("jmeno_souboru.csv", "r") do |soubor|
      soubor.each do |line|
       GLOB_POLE << Robot.new(line[0], line[1])
     end
   end

4 – Zápis do .csv je opět triviální.

    CSV.open("jmeno_souboru.csv", "w") do |soubor|
      Robot.roboti.each do |robot|
        soubor << "#{robot.jmeno}(#{robot.pozice[0]}x#{robot.pozice[1]}):#{robot.stav}"
      end
   end

V dalším článku se pak podíváme na xml a json.

Metoda s proměnným počtem argumentů v Ruby

Pro určení proměnného počtu argumentů v metodě se v Ruby používá znak hvězdičky (*).

Příklad

def myMethod(*arguments)
  arguments.each do |argument|
    puts argument
  end
end

myMethod
myMethod("a", "b", "c")

Výsledek

a
b
c

Další příklad

def myOtherMethod(arg1, arg2, *others)
  puts arg1
  puts arg2
  others.each do |other|
    puts other
  end
end

myOtherMethod(99, 1.123)
puts
myOtherMethod(123, "Ahoj", 24, 945612, "Nazdar")

Výsledek

99
1.123

123
Ahoj
24
945612
Nazdar

Význam otazníku v názvu Ruby metod

V programovacím jazyce Ruby je určitá zvyklost týkající se názvů metod a přidávání některých znaků na konec názvů metod. Velmi často se lze setkat s metodou, jejíž název končí otazníkem (?), jako např. u metody equal?. Otazník za názvem metody znamená, že metoda vrací datový typ Boolean (true nebo false).

Na příkladu jsou volány metody objektu Integer.

puts 1.equal?1
puts 2.even?
puts 3.odd?
puts 4.zero?
puts 5.between?(1, 10)
puts 6.integer?

Výsledek

true
true
true
false
true
true