Ruby解析處理YAML和json格式數(shù)據(jù)
Ruby處理YAML
Ruby的標(biāo)準(zhǔn)庫YAML基于Psych:https://ruby-doc.org/stdlib-2.6.2/libdoc/psych/rdoc/Psych.html
require 'yaml' 之后,為大多數(shù)的基本數(shù)據(jù)類型都提供了 to_ yaml() 方法,用于將各數(shù)據(jù)類型的對象轉(zhuǎn)換為yaml格式。
例如:
require 'yaml'
require 'set'
p "hello world".to_yaml
p 123.to_yaml
p %w(perl shell php).to_yaml
p ({one: 1, two: 2}).to_yaml
p Set.new([1,2,3]).to_yaml得到:
"--- hello world\n" "--- 123\n" "---\n- perl\n- shell\n- php\n" "---\n:one: 1\n:two: 2\n" "--- !ruby/object:Set\nhash:\n 1: true\n 2: true\n 3: true\n"
也可以使用YAML.dump()方法實(shí)現(xiàn)和to_yaml相同的功能,它還可以寫入文件。
users = [{name: 'Bob', permissions: ['Read']},
 {name: 'Alice', permissions:['Read', 'Write']}]
File.open("/tmp/a.yml","w") { |f| YAML.dump(users, f) }查看文件:
--- - :name: Bob #=> 注意,保留了hash源數(shù)據(jù)中的符號 :permissions: - Read - :name: Alice :permissions: - Read - Write
用YAML.load()從YAML中讀取數(shù)據(jù):
require 'yaml'
pp YAML.load(DATA)
__END__
mysql:
  passwd: P@ssword1!
  user: root
  port: 3306
  other1: nil
  other2: false
  other3: ""
  hosts: 
    - ip: 10.10.1.1
      hostname: node1
    - ip: 10.10.1.2
      hostname: node2得到:
{"mysql"=>
  {"passwd"=>"P@ssword1!",      #=> 注意,key是String而非Symbol
   "user"=>"root",
   "port"=>3306,
   "other1"=>"nil",
   "other2"=>false,
   "other3"=>"",
   "hosts"=>
    [{"ip"=>"10.10.1.1", "hostname"=>"node1"},
     {"ip"=>"10.10.1.2", "hostname"=>"node2"}]}}如果想讓hash的key是符號而非字符串,可以設(shè)置選項(xiàng)symbolize_names: true:
pp YAML.load(DATA, symbolize_names: true)
需要注意,YAML可以將對象進(jìn)行序列化,所以有幾方面注意事項(xiàng):
- 在反序列化的時(shí)候需要也require涉及到的文件,例如對Set類型序列化后,在反序列化時(shí)如不
require 'set'則無法還原對象 - 有些底層對象不能序列化,包括IO流、Ruby代碼對象Proc、Binding等
 - 不要反序列化不被信任的數(shù)據(jù)對象(比如用戶輸入的數(shù)據(jù)),此時(shí)可使用safe_load(),它默認(rèn)只允許加載以下幾種類型的數(shù)據(jù):
- TrueClass
 - FalseClass
 - NilClass
 - Numeric
 - String
 - Array
 - Hash
 
 - 如果確實(shí)想要加載額外的數(shù)據(jù)類型,可以在safe_load()中指定參數(shù)permitted_classes: []或permitted_symbols: []
 
Ruby處理Json數(shù)據(jù)
轉(zhuǎn)為json格式字符串
使用JSON.generate()可以將對象或數(shù)組轉(zhuǎn)換為JSON格式的數(shù)據(jù):
require 'json'
p JSON.generate "abc"
p JSON.generate 123
p JSON.generate true
p JSON.generate nil
p JSON.generate [2,3,4]
p JSON.generate({name: "junmajinlong", age: 23})
require 'set'
p JSON.generate(Set.new([1,23,44]))得到:
"\"abc\""
"123"
"true"
"null"
"[2,3,4]"
"{\"name\":\"junmajinlong\",\"age\":23}"
"\"#<Set: {1, 23, 44}>\""當(dāng)require 'json'后,很多ruby類型都具備了一個(gè)to_json的方法,可以直接將該類型的數(shù)據(jù)轉(zhuǎn)換為json數(shù)據(jù):
p ({name: "junmajinlong", age: 23}).to_json
p (Set.new([1,23,44])).to_json得到:
"{\"name\":\"junmajinlong\",\"age\":23}"
"\"#<Set: {1, 23, 44}>\""此外,JSON.dump()也可以將對象轉(zhuǎn)換為JSON格式的字符串,而且它還支持寫入文件:
hsh = {name: "junmajinlong", age: 23}
File.open("/tmp/a.json", "w") {|f| JSON.dump(hsh, f)}json格式字符串轉(zhuǎn)為Ruby對象
要從json格式字符串轉(zhuǎn)為ruby對象,有一些選項(xiàng)可設(shè)置,參考https://ruby-doc.org/stdlib-2.7.1/libdoc/json/rdoc/JSON.html#method-i-parse,比如*symbolize_names*選項(xiàng)表示是否將json object中的key解析為符號類型的key,如果設(shè)置為false,則解析為字符串的key。
要將json格式的字符串解析為Ruby數(shù)據(jù)類型(Hash),使用JSON.parse(),
require 'json'
hsh = '{"name": "junmajinlong", "age": 23}'
p JSON.parse(hsh)
p JSON.parse(hsh, symbolize_names: true)注意,上面的json字符串必須是合理的json數(shù)據(jù),比如key必須使用雙引號包圍而不能使用單引號,字符串必須使用雙引號包圍,等等。比如"{'name': 'junmajinlong', 'age': 23}"就不是合理的json字符串。
要從json文件中讀取json數(shù)據(jù)并轉(zhuǎn)換為Ruby數(shù)據(jù),使用load():
data = File.open("/tmp/a.json") do |f|
  JSON.load(f)
end
pp data
#=> {"name"=>"junmajinlong", "age"=>23}自定義對象的轉(zhuǎn)換方式
json支持的數(shù)據(jù)類型有:
- 字符串
 - 數(shù)值
 - 對象
 - 數(shù)組
 - 布爾
 - Null
 
從一種語言的數(shù)據(jù)轉(zhuǎn)換為Json數(shù)據(jù)時(shí),如果數(shù)據(jù)類型也是JSON所支持的,可直接轉(zhuǎn)換,但如果包含了JSON不支持的類型,則可能報(bào)錯(cuò),也可能以一種對象字符串的方式保存,這取決于對應(yīng)的實(shí)現(xiàn)。
可以在對象中定義as_json實(shí)例方法來決定對象如何轉(zhuǎn)換為json字符串,再定義類方法from_json()來決定如何從json字符串中恢復(fù)為一個(gè)對象。
例如,
require 'json'
require 'date'
class Person
  attr_accessor :name, :birthday
  def initialize name, birthday
    @name = name
    @birthday = DateTime.parse(birthday)
  end
end
File.open("/tmp/p.json", "w") do |f|
  JSON.dump(Person.new("junmajinlong", "1999-10-11"), f)
end查看保存的json數(shù)據(jù):
$ cat /tmp/p.json "#<Person:0x00007fffc7e575d0>"
定義as_json和frmo_json:
require 'json'
require 'date'
class Person
  attr_accessor :name, :birthday
  
  def initialize name, birthday
    @name = name
    @birthday = DateTime.parse(birthday)
  end
  
  def as_json
    {
      name: @name,
      birthday: @birthday.strftime("%F")
    }
  end
  def self.from_json json
    data = JSON.parse(json)
    new(data["name"], data["birthday"])
  end
end之后要序列化、反序列化該對象,可:
data = Person.new("junmajinlong", "1999-10-11").as_json
p data
p1=Person.from_json(JSON.dump data)
p p1.birthday如果是讀寫json文件,可:
person1 = Person.new("junmajinlong", "1999-10-11")
File.open("/tmp/p.json", "w") do |f|
  JSON.dump(person1.as_json, f)
end
p1 = File.open("/tmp/p.json") do |f|
  Person.from_json(f.read)
  # Person.from_json(JSON.load(f).to_json)
end
p p1幾種JSON解析工具的性能測試
測試了json標(biāo)準(zhǔn)庫、oj和fast_josnparser解析json的性能,測試項(xiàng)包括:
- 從文件中加載并解析json字符串為ruby對象
 - 從內(nèi)存json字符串中解析json字符串為ruby對象
 - 帶有symbolize_keys/symbolize_names轉(zhuǎn)換時(shí)的解析
 - json標(biāo)準(zhǔn)庫和oj將ruby對象dump為json字符串
 - json標(biāo)準(zhǔn)庫和oj將ruby對象dump為json字符串保存到文件
 
注:
- fast_jsonparser沒有dump功能,只有解析json字符串功能
 - oj在將對象轉(zhuǎn)換為json字符串時(shí),可能會(huì)丟失數(shù)據(jù)的精度,比如浮點(diǎn)數(shù)的精度
 
測試的json字符串?dāng)?shù)量大約50M。
測試了ruby 2.7.1和ruby 3.0.1兩個(gè)版本,gem包的版本信息如下:
fast_jsonparser (0.5.0) json (default: 2.5.1) oj (3.11.7)
測試代碼:
require 'benchmark'
require 'json'
require 'oj'
require 'fast_jsonparser'
# warm
json_file='test'  # 文件大小大約50M
str = File.read(json_file)
######## JSON
puts " load file ".center(80, '-')
Benchmark.bm(30) do |x|
  x.report("JSON.load:") { File.open(json_file){ |f| JSON.load(f) } }
  x.report("Oj.load_file:") { Oj.load_file(json_file) }
  x.report("FastJsonparser.load:") { FastJsonparser.load(json_file) }
end
puts
puts " load file with symbolize_keys ".center(80, '-')
Benchmark.bm(30) do |x|
  x.report("JSON.load:") { File.open(json_file){ |f| JSON.load(f, nil, symbolize_names: true, create_additions: false) } }
  x.report("Oj.load_file:") { Oj.load_file(json_file, symbol_keys: true) }
  x.report("FastJsonparser.load:") { FastJsonparser.load(json_file, symbolize_keys: true) }
end
puts
puts " parse str ".center(80, '-')
Benchmark.bm(30) do |x|
  x.report("JSON.parse:") { JSON.parse(str) }
  x.report("Oj.load:") { Oj.load(str) }
  x.report("FastJsonparser.parse:") { FastJsonparser.parse(str) }
end
puts
puts " parse str with symbolize_keys ".center(80, '-')
Benchmark.bm(30) do |x|
  x.report("JSON.parse:") { JSON.parse(str, symbolize_names: true) }
  x.report("Oj.load:") { Oj.load(str, symbol_keys: true) }
  x.report("FastJsonparser.parse:") { FastJsonparser.parse(str, symbolize_keys: true) }
end
obj = JSON.parse(str, symbolize_names: true)
puts 
puts " dump JSON to str ".center(80, '-')
Benchmark.bm(30) do |x|
  x.report("JSON.dump:") { JSON.dump(obj) }
  x.report("Oj.dump:") { Oj.dump(obj) }
end
puts 
puts " dump JSON to file ".center(80, '-')
Benchmark.bm(30) do |x|
  x.report("JSON.dump:") { File.open('0_json_dump', 'w') {|f| JSON.dump(obj, f) } }
  x.report("Oj.to_file:") { Oj.to_file('0_oj_dump', obj) }
end測試結(jié)果:
Ruby 2.7.1中:
---------------------------------- load file -----------------------------------
                                     user     system      total        real
JSON.load:                       1.591831   0.058021   1.649852 (  1.738119)
Oj.load_file:                    1.350385   0.057684   1.408069 (  2.434268) <-慢
FastJsonparser.load:             0.653968   0.103258   0.757226 (  0.848913) <-快
------------------------ load file with symbolize_keys -------------------------
                                     user     system      total        real
JSON.load:                       1.212617   0.039052   1.251669 (  1.349545)
Oj.load_file:                    1.432059   0.098950   1.531009 (  2.679610) <-慢
FastJsonparser.load:             0.695538   0.008384   0.703922 (  0.797081) <-快
---------------------------------- parse str -----------------------------------
                                     user     system      total        real
JSON.parse:                      1.343596   0.000000   1.343596 (  1.350368)
Oj.load:                         1.133612   0.000000   1.133612 (  1.140939)
FastJsonparser.parse:            0.701701   0.012340   0.714041 (  0.720296) <-快
------------------------ parse str with symbolize_keys -------------------------
                                     user     system      total        real
JSON.parse:                      1.250775   0.000000   1.250775 (  1.258796)
Oj.load:                         1.131296   0.000000   1.131296 (  1.138020)
FastJsonparser.parse:            0.697433   0.015962   0.713395 (  0.719439) <-快
------------------------------- dump JSON to str -------------------------------
                                     user     system      total        real
JSON.dump:                       1.374611   0.028454   1.403065 (  1.403081)
Oj.dump:                         1.025049   0.040184   1.065233 (  1.065246) <-快
------------------------------ dump JSON to file -------------------------------
                                     user     system      total        real
JSON.dump:                       1.234362   0.040246   1.274608 (  1.369214)
Oj.to_file:                      1.168707   0.000000   1.168707 (  1.270957)Ruby 3.0.1中:
---------------------------------- load file -----------------------------------
                                     user     system      total        real
JSON.load:                       1.362151   0.083610   1.445761 (  1.569754)
Oj.load_file:                    1.343601   0.182046   1.525647 (  2.684472) <-慢
FastJsonparser.load:             2.634435   0.052734   2.687169 (  2.776105) <-慢
------------------------ load file with symbolize_keys -------------------------
                                     user     system      total        real
JSON.load:                       1.287954   0.018572   1.306526 (  1.409770)
Oj.load_file:                    1.478750   0.043847   1.522597 (  2.668882) <-慢
FastJsonparser.load:             2.717857   0.006164   2.724021 (  2.822728) <-慢
---------------------------------- parse str -----------------------------------
                                     user     system      total        real
JSON.parse:                      1.242225   0.008661   1.250886 (  1.304554)
Oj.load:                         1.097922   0.000000   1.097922 (  1.110031)
FastJsonparser.parse:            2.602679   0.017232   2.619911 (  2.634604) <-慢
------------------------ parse str with symbolize_keys -------------------------
                                     user     system      total        real
JSON.parse:                      1.368262   0.000000   1.368262 (  1.380730)
Oj.load:                         1.332349   0.000000   1.332349 (  1.346331)
FastJsonparser.parse:            2.706804   0.007238   2.714042 (  2.726935) <-慢
------------------------------- dump JSON to str -------------------------------
                                     user     system      total        real
JSON.dump:                       1.724653   0.009250   1.733903 (  1.733912)
Oj.dump:                         1.298235   0.030041   1.328276 (  1.328279) <-快
------------------------------ dump JSON to file -------------------------------
                                     user     system      total        real
JSON.dump:                       1.765664   0.040595   1.806259 (  1.905785)
Oj.to_file:                      1.228744   0.020309   1.249053 (  1.349684) <-快
=end性能測試結(jié)論:
- (1).ruby 3之前,fast_jsonparser非??欤荝uby 3中的fast_jsonparser很慢
 - (2).OJ解析本地json字符串的性能比標(biāo)準(zhǔn)庫json性能稍好,但oj從文件中加載并解析json的速度很慢
 - (3).OJ將ruby對象解析為json字符串的效率比json標(biāo)準(zhǔn)庫性能好
 
即:
dump: Oj.dump > JSON.dump ruby3 之前: FastJsonparser.load > JSON.load > Oj.load_file FastJsonparser.parse > Oj.load > JSON.parse ruby3 之后: JSON.load > Oj.load_file > FastJsonparser.load Oj.load > JSON.parse > FastJsonparser.parse
multi_json
有一個(gè)名為multi_json的gem包,它提供多種json包的功能,默認(rèn)采用OJ作為json的適配引擎。它支持下面幾種json適配器:
- Oj Optimized JSON by Peter Ohler
 - Yajl Yet Another JSON Library by Brian Lopez
 - JSON The default JSON gem with C-extensions (ships with Ruby 1.9+)
 - JSON Pure A Ruby variant of the JSON gem
 - NSJSONSerialization Wrapper for Apple’s NSJSONSerialization in the Cocoa Framework (MacRuby only)
 - gson.rb A Ruby wrapper for google-gson library (JRuby only)
 - JrJackson JRuby wrapper for Jackson (JRuby only)
 - OkJson A simple, vendorable JSON parser
 
如果oj已被require,則默認(rèn)采用oj處理json,如果oj沒有被require,而是require了yajl,則采用yajl處理json,依次類推。
它提供了load()和dump()方法:
load(json_str, options = {})
  options: 
    symbolize_keys: true, false
    adapter:  oj, json_gem, yajl, json_pure, ok_json
dump(object, options = {})更多關(guān)于Ruby處理操作YAML和Ruby處理操作json方法請查看下面的相關(guān)鏈接
相關(guān)文章
 Monkey Patch猴子補(bǔ)丁編程方式及其在Ruby中的運(yùn)用
Monkey Patch是指在程序運(yùn)行時(shí)追加代碼段,一般被認(rèn)為是解釋型語言的特長,下面我們就來看一下Monkey Patch猴子補(bǔ)丁編程方式及其在Ruby中的運(yùn)用2016-05-05
 用Ruby實(shí)現(xiàn)一個(gè)單元測試框架的教程
這篇文章主要介紹了用Ruby實(shí)現(xiàn)一個(gè)單元測試框架的教程,在檢測Ruby代碼bug的時(shí)候非常有用,需要的朋友可以參考下2015-04-04
 Rails bundle命令安裝mysql gem包出錯(cuò)的解決方法
這篇文章主要介紹了Rails bundle命令安裝mysql gem包出錯(cuò)的解決方法,本文原因是沒有安裝mysql開發(fā)包,需要的朋友可以參考下2014-08-08
 在Docker中自動(dòng)化部署Ruby on Rails的教程
這篇文章主要介紹了在Docker中部署Ruby on Rails的教程,Docker是當(dāng)下最火的虛擬機(jī),而本文所介紹的Ruby on Rails的部署則充分利用了Ruby中的rake這一炫酷的實(shí)現(xiàn)自動(dòng)化的方法,需要的朋友可以參考下2015-06-06
 Ruby在cmd下中文顯示亂碼以及不支持OpenSSL的問題解決
這篇文章主要介紹了Ruby在cmd下中文顯示亂碼以及不支持OpenSSL的問題解決,其中OpenSSL的相關(guān)編譯操作在Linux環(huán)境下進(jìn)行,需要的朋友可以參考下2015-11-11
 Ruby面向?qū)ο缶幊讨蓄惻c方法的基礎(chǔ)學(xué)習(xí)
方法可以理解為類中的函數(shù),一定程度上可以受到類作用域的制約,其他地方和傳統(tǒng)意義上的函數(shù)無太大區(qū)別,這里我們就一起來進(jìn)行Ruby面向?qū)ο缶幊讨蓄惻c方法的基礎(chǔ)學(xué)習(xí)2016-05-05
 Ruby環(huán)境下安裝使用bundler來管理多版本的gem
這篇文章主要介紹了Ruby環(huán)境下安裝使用bundler來管理多版本的gem的方法,舉了Ruby On Rails中的應(yīng)用實(shí)例來進(jìn)行演示,需要的朋友可以參考下2016-06-06

