Parsing JSON in Ruby (like XPATH)

I have a JSON document returned from a query to the Google Books API, e.g:

{ "items": [ { "volumeInfo": { "industryIdentifiers": [ { "type": "OTHER", "identifier": "OCLC:841804665" } ] } }, { "volumeInfo": { "industryIdentifiers": [ { "type": "ISBN_10", "identifier": "156898118X"...

I need the ISBN number (type: ISBN_10 or ISBN_13) and I've written a simple loop that traverses the parsed JSON (parsed = json.parse(my_uri_response)). In this loop, I have a next if k['type'] = "OTHER" which sets "type" to "OTHER".

How do I best extract just one ISBN number from my JSON example? Not all of them, just one.

Something like XPath search would be helpful.

1

2 Answers

JSONPath may be just what you're looking for:

require 'jsonpath'
json = #your raw JSON above
path = JsonPath.new('$..industryIdentifiers[?(@.type == "ISBN_10")].identifier')
puts path.on(json)

Result:

156898118X

See this page for how XPath translates to JSONPath. It helped me determine the JSONPath above.

2

how about:

parsed['items'].map { |book| book['volume_info']['industryIdentifiers'].find{ |prop| ['ISBN_10', 'ISBN_13'].include? prop['type'] }['identifier']
}

If you receive undefined method [] for nil:NilClass this means that you have an element within items array, which has no volume_info key, or that you have a volume with a set of industryIdentifiers without ISBN. Code below should cover all those cases (+ the case when you have volumeInfo without industry_identifiers:

parsed['items'].map { |book| identifiers = book['volume_info'] && book['volume_info']['industryIdentifiers'] isbn_identifier = idetifiers && identifiers.find{ |prop| ['ISBN_10', 'ISBN_13'].include? prop['type']}['identifier'] } isbn_identifier && isbn_identifier['identifier']
}.compact

If you happen to have the andand gem, this might be written as:

parsed['items'].map { |book| book['volume_info'].andand['industryIdentifiers'].andand.find{ |prop| ['ISBN_10', 'ISBN_13'].include? prop['type'] }.andand['identifier']
}.compact

Note that this will return only one ISBN for each volume. If you have volumes with both ISBN_10 and ISBN_13 and you want to get both, instead of find you'll need to use select method and .map{|i| i[:identifier]} in place of .andand['identifier'].

0

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like