Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.3k views
in Technique[技术] by (71.8m points)

ruby - How to convert JSON to a hash, search for and change a value

I'm trying to process a JSON file:

{
  "features": {
    "additional-options": true
  },
  "values": {
    "lo-value": 34,
    "hi-value": 554
  },
  "persons": [
    {
      "name": "john",
      "member": true,
      "current": false,
      "sponsor": "pete",
      "profile": "",
      "credits": ["04"],
      "linked": ["philip","guy"],
      "maptools": ["crossfit","soccer","running"]
    },
    {
      "name": "mary",
      "member": true,
      "current": false,
      "sponsor": "judy",
      "profile": "",
      "credits": ["all"],
      "activities": ["swimming","cycling","running"]
    }
  ],
  "data_map": [1122,3234]
}

I would like to be able to:

  • Update a value on a key value pair
  • Delete both the key/value
  • Delete or insert a value that's an array

I have tried so many thing to get this right.

My simplified code idea is:

require 'json'

hash = JSON.parse(File.read('test.json'))

# Add key & value or change existing one
def change_key(hash, key, value)
    keys = key.strip(".")
    hash[*keys] = value
end

def add_to_array(hash, key, val)
    keys = key.strip(".")
    hash[*keys] = locate_arr(hash, key).insert(val)
end

# Delete a key and it's value
def del_key(hash, key)
    keys = key.strip(".")
    hash[*keys].delete[-1]
end

def del_from_array(hash, key, val)
    keys = key.strip(".")
    hash[*keys] = locate_arr(hash, key).delete[-1]
end

f = File.write('test.json') ; f.puts JSON.pretty_generate(hash)


change_key(hash, "features.additional-options", false)

del_from_array(hash, "persons.name=mary.activities", "cycling")

add_to_array(hash, "persons.name=mary.activities", "hockey")

del_key(hash, "data_map")

del_key(hash, persons.name=john.profile)

del_key(hash, persons.name=mary.credits)

The resulting JSON should be:

{
  "features": {
    "additional-options": false
  },
  "values": {
    "lo-value": 34,
    "hi-value": 554
  },
  "persons": [
    {
      "name": "john",
      "member": true,
      "current": false,
      "sponsor": "pete",
      "credits": ["04"],
      "linked": ["philip","guy"],
      "maptools": ["crossfit","soccer","running"]
    },
    {
      "name": "mary",
      "member": true,
      "current": false,
      "sponsor": "judy",
      "profile": "",
      "activities": ["swimming", "running","hockey"]
    }
  ]
}

I'm not sure how to work with JSON that is structured like this.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I understand your JSON may look like this:

"{"features":{"additional-options":true},"values":{"lo-value":34,"hi-value":554},"persons":[{"name":"john","member":true,"current":false,"sponsor":"pete","profile":"","credits":["04"],"linked":["philip","guy"],"maptools":["crossfit","soccer","running"]},{"name":"mary","member":true,"current":false,"sponsor":"judy","profile":"","credits":["all"],"activities":["swimming","cycling","running"]}],"data_map":[1122,3234]}"

I suggest using an OpenStruct to organize your data:

your_struct_name =  JSON.parse(yourJson, object_class: OpenStruct)

Then you get all the things you want. For the operations you show:

#change_key(hash, "features.additional-options", false)
your_struct_name.features['additional-options'] = false 
#this one above you set in this hash-like manner because of the '-' in the middle of the key. Otherwise you could just do your_struct_name.features.additional_options = false

#del_from_array(hash, "persons.name=mary.activities", "cycling")
your_struct_name.persons.last.activities.delete('swimming')

# or selecting by name:
your_struct_name.persons.select {|person| person.name == 'mary' }.first.activities.delete('swimming')

#add_to_array(hash, "persons.name=mary.activities", "hockey")
your_struct_name.persons.last.activities << 'hockey'

#del_key(hash, "data_map")
your_struct_name.delete_field('data_map')

#del_key(hash, persons.name=john.profile)
...

#del_key(hash, persons.name=mary.credits)
...

Then, after you make your changes, you can use:

your_struct_name.to_h.to_json

You can also use the method as_json to get a structure very similar to what you showed on the question:

your_struct_name.as_json

OpenStruct is very nice to deal with data that has a changing structure. If you have data that can be "modeled", has a name you can call, has some attributes you can predict, and even methods you will use for this data, I suggest you to create a Class to describe this data, its properties and attributes (it can even inherit from OpenStruct). Then work inside this Class domain, creating a layer of abstraction. This way your code gets a lot more robust and readable. Don't forget to create automatic tests! It saves you a lot of time.

The way you organize and abstract your data, and specially the way you name entities are things that have high impact on code quality.

For further reading see: Object and ActiveData.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...