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.
12 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:
156898118XSee this page for how XPath translates to JSONPath. It helped me determine the JSONPath above.
2how 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']
}.compactIf 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']
}.compactNote 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'].