0

I want a single method which will update the content of Json which should work like below:

data = { "id"=>7913251, "domain"=>"domain.com", "ready"=>true, "create_starttime"=>"2018-08-30 05:57:14 -0500" }

update_json(data, 'profile/zones/0/esxi/1')

In the above the keys zones and esxi are Array in hash. Example josn content below

{"profile"=>
  {"zones"=>
    [{"name"=>"cloud_group1",
      "is_main"=>true,
      "esxi"=>
       [{"id"=>7923451,
         "domain"=>"domain.com",
         "ready"=>true,
         "create_starttime"=>"2018-08-30 05:57:14 -0500",
         "create_stoptime"=>"2018-08-30 07:29:05 -0500"}]
    }]
  }
}

The following code works when there is no array in hash.

def update_json(data, path)
        components = path.split('/')
        if components.length > 1
          key = components[-1]
          path.slice! "/#{key}"
          find.tree(path)[key] = data
        else
          find.tree[path] = data
        end
        File.open(json_string, 'w') { |file| file.write(JSON.pretty_generate(find.tree)) }
      end # update_json

In the above code find.tree is my method which returns value inside the path for eg., 'profile/zones', if no args passed it will return entire json content

Bharath M
  • 128
  • 1
  • 3
  • 14
  • Could you describe how it's not working? I assume you get `TypeError: no implicit conversion of String into Integer`. Is that correct? – wteuber Jul 31 '19 at 21:25
  • find.tree is my custom method it only works/traverse thru the path as long as it is a hash and does not encounter Array. So in the above case when i have to add one more entry to esxi which is an array so does zones my function wont work. So need your help in modifying the above code so it also works with Array. – Bharath M Aug 01 '19 at 06:01
  • I see. If you are looking for a generic solution, you will need to check if the current value is a hash (keep traversing with key), an array (keep traversing with index). I think https://stackoverflow.com/a/57298997/1288687 will help you fix that. If you don't want to use `dig`, check the type of your currently traversed value using a case switch or if statement `.is_a?(Array)` to decide how to continue. – wteuber Aug 03 '19 at 06:16

1 Answers1

0

I have no visibility of the method tree(), but I would approach the task like this example:

hash.dig('profile', 'zones', 0, 'esxi')[0] = data

When dealing with arrays, you would have to not only split path but also convert indexes to numbers. If find.tree() works properly,

key = Integer(key) if key[/^\d+$/]
find.tree(path)[key] = data

should do the job. Please note, that the condition is useless if your paths contains numbers that are not indexes of an array. Also watch out when assigning hashes. You might want to ensure your default_proc is setup correctly: https://stackoverflow.com/a/34621043/1288687

I hope you find this helpful.

wteuber
  • 1,208
  • 9
  • 15