不使用 stdlib 将 Ruby 哈希数组转换为 JSON(漂亮)?

Convert array of Ruby hashes to JSON (pretty) not using stdlib?

根据问题,只是想知道如何在不使用 Ruby stdlib 'JSON' 模块(以及 JSON.pretty_generate 方法)的情况下执行此操作。

所以我有一个哈希数组,如下所示:

[{"h1"=>"a", "h2"=>"b", "h3"=>"c"}, {"h1"=>"d", "h2"=>"e", "h3"=>"f"}]

我想将其转换为如下所示:

[
  {
    "h1": "a",
    "h2": "b",
    "h3": "c",
  },
  {
    "h1": "d",
    "h2": "e",
    "h3": "f",
  }
]

我可以使用简单的 gsub (array_of_hashes.to_s.gsub!(/=>/, ": ")) 将哈希火箭替换为冒号空格,但不确定如何生成它以便它看起来像上面的例子。我最初想过使用 here-doc 方法来执行此操作,但不确定这是最好的方法,而且我还没有设法让它工作。我是 Ruby 的新手,如果这很明显,我深表歉意! :-)

def to_json_pretty
        json_pretty = <<-EOM
[
  {
    "#{array_of_hashes.each { |hash| puts hash } }"
  },
]
EOM
        json_pretty
      end

如果您输入的格式与您提供的格式完全一致,而不是嵌套,那么这应该能正常工作:

a =  [{"h1"=>"a", "h2"=>"b", "h3"=>"c"}, {"h1"=>"d", "h2"=>"e", "h3"=>"f"}]
hstart = 0
astart = 0
a.each do |b|
  puts "[" if astart == 0
  astart+=1
  b.each do |key, value|
    puts " {" if hstart == 0 
    hstart += 1
    puts "   " + key.to_s + ' : ' + value
    if hstart % 2 == 0 
      if hstart ==  a.collect(&:size).reduce(:+)
        puts " }"  
      else
        puts " },\n {"
      end
    end
  end
  puts "]" if astart == a.size
end

输出:

[
 {
   h1 : a
   h2 : b
 },
 {
   h3 : c
   h1 : d
 },
 {
   h2 : e
   h3 : f
 }
]

一般来说,要在不使用库的情况下很好地使用 JSON 需要的不仅仅是几行代码。话虽如此,JSON 化事物的最佳方式通常是递归地进行,例如:

def pretty_json(obj)
  case obj
  when Array
    contents = obj.map {|x| pretty_json(x).gsub(/^/, "  ") }.join(",\n")
    "[\n#{contents}\n]"
  when Hash
    contents = obj.map {|k, v| "#{pretty_json(k.to_s)}: #{pretty_json(v)}".gsub(/^/, "  ") }.join(",\n")
    "{\n#{contents}\n}"
  else
    obj.inspect
  end
end

你可以看看我的NeatJSON gem for how I did it. Specifically, look at neatjson.rb,它使用递归解决方案(通过过程)。

根据您提供的格式化选项,我的代码有很多变化,因此它显然不必如此复杂。但一般模式是测试提供给 method/proc 的对象类型,如果它很简单则将其序列化,或者(如果它是数组或散列)为其中的每个值重新调用 method/proc。

这是一个大大简化的版本(没有缩进、没有换行、硬编码间距):

def simple_json(object)
  js = ->(o) do
    case o
      when String               then o.inspect
      when Symbol               then o.to_s.inspect
      when Numeric              then o.to_s
      when TrueClass,FalseClass then o.to_s
      when NilClass             then "null"
      when Array                then "[ #{o.map{ |v| js[v] }.join ', '} ]"
      when Hash                 then "{ #{o.map{ |k,v| [js[k],js[v]].join ":"}.join ', '} }"
      else
        raise "I don't know how to deal with #{o.inspect}"
    end
  end
  js[object]
end


puts simple_json({a:1,b:[2,3,4],c:3})
#=> { "a":1, "b":[ 2, 3, 4 ], "c":3 }