Techioz Blog

多次元ハッシュ値をjsonに変換する方法

概要

テーブル列 backend_notes にハッシュ値があり、このハッシュを json 形式に変更する必要があります。

{"school_id"=>"6", "accounts_response"=>"{\"type\"=>\"success\", \"message\"=>\"Updated Courses for 56789\"}", "courses
_not_found"=>"[{\"term\"=>\"FALL 2019\", \"dept_code\"=>\"ACCT\", \"dept_name\"=>\"ACCOUNTING\", \"course_code\"=>\"102\", \"course_name\"=>\"ELEM
ENTARY ACCT\", \"section_code\"=>\"000\", \"status\"=>\"new\", \"id\"=>20379}]"}

以下の移行コマンドを試してハッシュをjsonに変更しました

change_column :carts, :backend_notes,'jsonb USING CAST(backend_notes AS jsonb)'

しかし、今回の移行では以下のような形式に変更されました。

{"school_id":"6", "accounts_response":"{\"type\"=>\"success\", \"message\"=>\"Updated Courses for 56789\"}", "courses
_not_found":"[{\"term\"=>\"FALL 2019\", \"dept_code\"=>\"ACCT\", \"dept_name\"=>\"ACCOUNTING\", \"course_code\"=>\"102\", \"course_name\"=>\"ELEM
ENTARY ACCT\", \"section_code\"=>\"000\", \"status\"=>\"new\", \"id\"=>20379}]"} 

私の期待される出力は以下のようなものです

{"school_id":"6","accounts_response":{"type":"success","message":"Updated Courses for 56789"},"courses_not_found":{"term":"FALL 2019","dept_code":"ACCT","dept_name":"ACCOUNTING","course_code":"102","course_name":"ELEMENTARY ACCT","section_code":"000","status":"new","id":"20379"}}

解決策

DB のハッシュ値には、Ruby オブジェクト (配列とハッシュ) の文字列表現である値が含まれます。これらの値を JSON に完全に変換するには、まず文字列を Ruby オブジェクトに変換する必要があります。

非常に簡単な方法は、 => を : に置き換え、JSON パーサーを使用して変換を行うことです。それが機能するかどうか、またはこれらの値を修正するためにより複雑なロジックが必要かどうかは、内部構造によって異なります。次のようなヘルパー メソッドから始めます。

value = {"school_id"=>"6", "accounts_response"=>'{"type"=>"success", "message"=>"Updated Courses for 56789"}', "courses_not_found"=>'[{"term"=>"FALL 2019", "dept_code"=>"ACCT", "dept_name"=>"ACCOUNTING", "course_code"=>"102", "course_name"=>"ELEM ENTARY ACCT", "section_code"=>"000", "status"=>"new", "id"=>20379}]'}

# helper method
require 'json'

def string_parse_to_hash(string)
  modified_string = string
    .gsub(/:(\w+)/){"\"#{$1}\""}
    .gsub('=>', ':')
    .gsub("nil", "null")
  JSON.parse(modified_string)
rescue
  {}
end

# translate strings values to Ruby objects
value['accounts_response'] = string_parse_to_hash(value['accounts_response'])
value['courses_not_found'] = string_parse_to_hash(value['courses_not_found'])

value
#=> {
      "school_id"=>"6",
      "accounts_response"=>{"type"=>"success", "message"=>"Updated Courses for 56789"},
      "courses_not_found"=>[
        {"term"=>"FALL 2019",
          "dept_code"=>"ACCT",
          "dept_name"=>"ACCOUNTING",
          "course_code"=>"102",
          "course_name"=>"ELEM ENTARY ACCT",
          "section_code"=>"000",
          "status"=>"new",
          "id"=>20379
        }
      ]
    }

# translate to JSON
value.to_json
#=> "{\"school_id\":\"6\",\"accounts_response\":{\"type\":\"success\",\"message\":\"Updated Courses for 56789\"},\"courses_not_found\":[{\"term\":\"FALL 2019\",\"dept_code\":\"ACCT\",\"dept_name\":\"ACCOUNTING\",\"course_code\":\"102\",\"course_name\":\"ELEM ENTARY ACCT\",\"section_code\":\"000\",\"status\":\"new\",\"id\":20379}]}"