Search code examples
ruby-on-railsrubypostgresql

How to change multi dimensional hash value into json


I have a hash value in my table column backend_notes and I need to change this hash into json format.

{"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}]"}

I tried the below migration command to change hash to json

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

but this migration changed like this below format.

{"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}]"} 

My expected output is like below

{"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"}}

Solution

  • The hash value from your DB includes values that are string representations of Ruby objects (arrays and hashes). To fully translate those values to JSON you need to translate the strings into Ruby objects first.

    A very simple way might be to replace the => with : and use a JSON parser to do the translation. It depends on the internal structure if that works or if you need more complex logic to fix those values. I would start with a helper method like this:

    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}]}"