├── .gitignore ├── LICENSE ├── README.md ├── demo └── demo.js ├── helpers.js ├── index.js ├── package-lock.json ├── package.json └── transformer ├── index.js └── mappings.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Mukul Jain 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # goodreads-json-api 2 | Library to get Goodreads API response in JSON format 3 | 4 | # Project scope 5 | 6 | The reason I build this project, was because I needed a way to get data from Goodreads API as json. And most of the API returns their data as XML. 7 | 8 | This packages don't call goodreads apis internally, it just takes the XML response and returns the parse JSON object. 9 | For xml parsing, I've used [cheerio](https://www.npmjs.com/package/cheerio) project. 10 | 11 | # Usage 12 | 13 | Install the package 14 | 15 | ``` 16 | npm install --save goodreads-json-api 17 | ``` 18 | 19 | ``` 20 | const goodReadsJSONResponse = require(goodreads-json-api); 21 | 22 | https.get('https://www.goodreads.com/book/isbn/0441172717?key=Uxb0zPb86N4STVy2ECWYA', (res) => { 23 | const options = { 24 | xml: { 25 | normalizeWhitespace: true 26 | } 27 | } 28 | const statusCode = res.statusCode; 29 | const contentType = res.headers['content-type']; 30 | let error; 31 | if (statusCode !== 200) { 32 | error = new Error('Request Failed.\n' + 33 | `Status Code: ${statusCode}`); 34 | } 35 | if (error) { 36 | console.log(error.message); 37 | // consume response data to free up memory 38 | res.resume(); 39 | return; 40 | } 41 | 42 | res.setEncoding('utf8'); 43 | let rawData = ''; 44 | res.on('data', (chunk) => rawData += chunk); 45 | res.on('end', () => { 46 | try { 47 | const resp = goodReadsJSONResponse.convertToJson(rawData); 48 | console.log(resp) 49 | } catch (e) { 50 | console.log(e.message); 51 | } 52 | }); 53 | }).on('error', (e) => { 54 | console.log(`Got error: ${e.message}`); 55 | }); 56 | ``` 57 | 58 | Run `npm run demo` from this project to see the [demo](https://github.com/myke11j/goodreads-json-api/blob/master/demo/demo.js) file running. 59 | 60 | # Contribute 61 | 62 | I would greatly appreciate if you can also contribute to this project in any way. 63 | 64 | Currently this converts following xml nodes of Goodreads response: 65 | - author 66 | - books 67 | - similiar_books 68 | - request 69 | - popular_shelves 70 | 71 | For every node, we've a transformer, which you can find [here](https://github.com/myke11j/goodreads-json-api/blob/master/transformer/mappings.js). These transformers are simple json which converts xml to json. 72 | You can add any other node, which I've missed here and create a PR. -------------------------------------------------------------------------------- /demo/demo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc Demo file showing working of this package. Run node demo.js from demo directory to see json response of API 3 | * @author Mukul <@mukul1904> 4 | */ 5 | 6 | 'use strict' 7 | 8 | const https = require('https'); 9 | const goodReadsJSONResponse = require('../index'); 10 | 11 | const author = 'Neil Gaiman'; 12 | const book = 'Neverwhere'; 13 | 14 | // const API = `https://www.goodreads.com/book/title.xml?author=${author}&key=Uxb0zPb86N4STVy2ECWYA&title=${book}`; 15 | const API = 'https://www.goodreads.com/book/title.xml?key=Uxb0zPb86N4STVy2ECWYA&title=harry potter '; 16 | // const API = 'https://www.goodreads.com/book/isbn/0441172717?key=Uxb0zPb86N4STVy2ECWYA'; 17 | 18 | https.get(API, (res) => { 19 | res.setEncoding('utf8'); 20 | let rawData = ''; 21 | res.on('data', (chunk) => rawData += chunk); 22 | res.on('end', () => { 23 | const response = goodReadsJSONResponse.convertToJson(rawData); 24 | console.log(response); 25 | }); 26 | }).on('error', (e) => { 27 | console.log(`Got error: ${e.message}`); 28 | }); -------------------------------------------------------------------------------- /helpers.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const helpers = {}; 4 | 5 | 6 | helpers.stripCDATA = (cDataISBN) => { 7 | const regex = //g; 8 | let m; 9 | let matched; 10 | while ((m = regex.exec(cDataISBN)) !== null) { 11 | // This is necessary to avoid infinite loops with zero-width matches 12 | if (m.index === regex.lastIndex) { 13 | regex.lastIndex++; 14 | } 15 | 16 | // The result can be accessed through the `m`-variable. 17 | m.forEach((match, groupIndex) => { 18 | matched = match; 19 | }); 20 | } 21 | return matched; 22 | } 23 | 24 | helpers.newStripCDATA = (text) => { 25 | if (!text) return text; 26 | if (text.indexOf(')/g); 30 | return (matches) ? matches[0] : text; 31 | } 32 | 33 | helpers.getPopularCategories = (popularShelves) => { 34 | if (popularShelves.length > 20) { 35 | popularShelves = popularShelves.slice(0, 20); 36 | } 37 | const shelves = []; 38 | popularShelves.map((shelve) => { 39 | shelves.push({ 40 | count: popularShelves[shelve].attribs.count, 41 | name: popularShelves[shelve].attribs.name 42 | }); 43 | }); 44 | return shelves; 45 | } 46 | 47 | helpers.stripHTML = (text) => { 48 | if (!text) return text; 49 | if (text.indexOf(')/g); 53 | text = (matches) ? matches[0] : text; 54 | return text.replace(/<\/?[^>]+(>|$)/g, ""); 55 | } 56 | 57 | module.exports = helpers; -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const cheerio = require('cheerio'); 4 | const async = require('async'); 5 | const transformer = require('./transformer'); 6 | const helpers = require('./helpers'); 7 | const goodReadsJSONResponse = {}; 8 | const options = { 9 | xml: { 10 | normalizeWhitespace: true 11 | } 12 | }; 13 | 14 | const transform = (params) => { 15 | const { $, transformer, doc, prevNode } = params; 16 | let parentNode = ''; 17 | if (prevNode) { 18 | parentNode += prevNode + ' '; 19 | } 20 | transformer.mapping.forEach((mapItem) => { 21 | const xmlNode = parentNode + transformer.type + ' ' + mapItem.key; 22 | // console.log(xmlNode, $(xmlNode).html()); 23 | doc[mapItem.jsonKey] = $(xmlNode).html(); 24 | if (mapItem.helper && helpers[mapItem.helper]) { 25 | doc[mapItem.jsonKey] = helpers[mapItem.helper](doc[mapItem.jsonKey]); 26 | } 27 | }); 28 | }; 29 | 30 | const transformList = (params) => { 31 | const { $, transformer, doc, prevNode } = params; 32 | let parentNode = ''; 33 | if (prevNode) { 34 | parentNode += prevNode + ' '; 35 | } 36 | const count = $(parentNode).find(transformer.type).slice(0, 30); 37 | for (var index = 0; index < count.length; index++) { 38 | const dummyDoc = {}; 39 | transformer.mapping.forEach((mapItem) => { 40 | dummyDoc[mapItem.jsonKey] = $(parentNode).find(transformer.type)[index].attribs[mapItem.key]; 41 | if (mapItem.helper && helpers[mapItem.helper]) { 42 | dummyDoc[mapItem.jsonKey] = helpers[mapItem.helper](dummyDoc[mapItem.jsonKey]); 43 | } 44 | }); 45 | doc[prevNode].push(dummyDoc); 46 | } 47 | } 48 | 49 | const transformBooks = function (params) { 50 | const { $, transformer, prevNode, doc, rootTtransformer } = params; 51 | let parentNode = ''; 52 | if (prevNode) { 53 | parentNode += prevNode + ' '; 54 | } 55 | const similiarBooks = $(parentNode).html().toString().split('').slice(0, 5); 56 | async.eachSeries(similiarBooks, (element, cb) => { 57 | if (element.length === 1) return cb(); 58 | let book = {}; 59 | transform({ 60 | transformer, doc: book, $: cheerio.load('' + element, options) 61 | }); 62 | doc.push(book); 63 | cb(); 64 | }, (err) => { 65 | return true; 66 | }); 67 | } 68 | 69 | goodReadsJSONResponse.convertToJson = (xmlData) => { 70 | const $ = cheerio.load(xmlData, options); 71 | let response = {}; 72 | transformer.mappings.forEach((item, index) => { 73 | const type = item.type; 74 | let element = type; 75 | if ($(element).html() === null) return; 76 | // const resp = response[element]; 77 | const mapping = item.mapping; 78 | mapping.forEach((mapItem, index2) => { 79 | switch (element) { 80 | case 'popular_shelves': 81 | if ($('popular_shelves').find('shelf').length <= 0) return; 82 | response[element] = []; 83 | transformList({ 84 | transformer: transformer.mappings.filter(mapping => mapping.type === 'shelf')[0], 85 | doc: response, 86 | $, 87 | prevNode: 'popular_shelves' 88 | }); 89 | break; 90 | case 'similar_books': 91 | if ($('similar_books').find('book').length <= 0) return; 92 | response[element] = []; 93 | const cloneDoc = JSON.parse(JSON.stringify(response[element])); 94 | transformBooks({ 95 | transformer: transformer.mappings.filter(mapping => mapping.type === 'book')[0], 96 | doc: cloneDoc, 97 | $, 98 | prevNode: 'similar_books', 99 | rootTtransformer: transformer 100 | }); 101 | response[element] = cloneDoc; 102 | break; 103 | default: 104 | response[element] = {}; 105 | const cloneDoc2 = JSON.parse(JSON.stringify(response[element])); 106 | transform({ 107 | transformer: item, doc: cloneDoc2, $ 108 | }); 109 | response[element] = cloneDoc2; 110 | break; 111 | } 112 | }); 113 | }); 114 | return response; 115 | }; 116 | // 117 | module.exports = goodReadsJSONResponse; 118 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "goodreads-json-api", 3 | "version": "1.0.4", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/node": { 8 | "version": "8.0.57", 9 | "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.57.tgz", 10 | "integrity": "sha512-ZxrhcBxlZA7tn0HFf7ebUYfR9aHyBgjyavBLzyrYMYuAMbONBPY4S5O35562iV2FfwnZCjQky3gTDy1B3jSZ2Q==" 11 | }, 12 | "async": { 13 | "version": "2.6.0", 14 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", 15 | "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", 16 | "requires": { 17 | "lodash": "4.17.4" 18 | } 19 | }, 20 | "boolbase": { 21 | "version": "1.0.0", 22 | "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", 23 | "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" 24 | }, 25 | "cheerio": { 26 | "version": "1.0.0-rc.2", 27 | "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.2.tgz", 28 | "integrity": "sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs=", 29 | "requires": { 30 | "css-select": "1.2.0", 31 | "dom-serializer": "0.1.0", 32 | "entities": "1.1.1", 33 | "htmlparser2": "3.9.2", 34 | "lodash": "4.17.4", 35 | "parse5": "3.0.3" 36 | } 37 | }, 38 | "core-util-is": { 39 | "version": "1.0.2", 40 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 41 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 42 | }, 43 | "css-select": { 44 | "version": "1.2.0", 45 | "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", 46 | "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", 47 | "requires": { 48 | "boolbase": "1.0.0", 49 | "css-what": "2.1.0", 50 | "domutils": "1.5.1", 51 | "nth-check": "1.0.1" 52 | } 53 | }, 54 | "css-what": { 55 | "version": "2.1.0", 56 | "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", 57 | "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=" 58 | }, 59 | "dom-serializer": { 60 | "version": "0.1.0", 61 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", 62 | "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", 63 | "requires": { 64 | "domelementtype": "1.1.3", 65 | "entities": "1.1.1" 66 | }, 67 | "dependencies": { 68 | "domelementtype": { 69 | "version": "1.1.3", 70 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", 71 | "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=" 72 | } 73 | } 74 | }, 75 | "domelementtype": { 76 | "version": "1.3.0", 77 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", 78 | "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=" 79 | }, 80 | "domhandler": { 81 | "version": "2.4.1", 82 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.1.tgz", 83 | "integrity": "sha1-iS5HAAqZvlW783dP/qBWHYh5wlk=", 84 | "requires": { 85 | "domelementtype": "1.3.0" 86 | } 87 | }, 88 | "domutils": { 89 | "version": "1.5.1", 90 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", 91 | "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", 92 | "requires": { 93 | "dom-serializer": "0.1.0", 94 | "domelementtype": "1.3.0" 95 | } 96 | }, 97 | "entities": { 98 | "version": "1.1.1", 99 | "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", 100 | "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=" 101 | }, 102 | "htmlparser2": { 103 | "version": "3.9.2", 104 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", 105 | "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", 106 | "requires": { 107 | "domelementtype": "1.3.0", 108 | "domhandler": "2.4.1", 109 | "domutils": "1.5.1", 110 | "entities": "1.1.1", 111 | "inherits": "2.0.3", 112 | "readable-stream": "2.3.3" 113 | } 114 | }, 115 | "inherits": { 116 | "version": "2.0.3", 117 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 118 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 119 | }, 120 | "isarray": { 121 | "version": "1.0.0", 122 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 123 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 124 | }, 125 | "lodash": { 126 | "version": "4.17.4", 127 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", 128 | "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" 129 | }, 130 | "nth-check": { 131 | "version": "1.0.1", 132 | "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", 133 | "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", 134 | "requires": { 135 | "boolbase": "1.0.0" 136 | } 137 | }, 138 | "parse5": { 139 | "version": "3.0.3", 140 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", 141 | "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", 142 | "requires": { 143 | "@types/node": "8.0.57" 144 | } 145 | }, 146 | "process-nextick-args": { 147 | "version": "1.0.7", 148 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", 149 | "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" 150 | }, 151 | "readable-stream": { 152 | "version": "2.3.3", 153 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", 154 | "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", 155 | "requires": { 156 | "core-util-is": "1.0.2", 157 | "inherits": "2.0.3", 158 | "isarray": "1.0.0", 159 | "process-nextick-args": "1.0.7", 160 | "safe-buffer": "5.1.1", 161 | "string_decoder": "1.0.3", 162 | "util-deprecate": "1.0.2" 163 | } 164 | }, 165 | "safe-buffer": { 166 | "version": "5.1.1", 167 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 168 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" 169 | }, 170 | "string_decoder": { 171 | "version": "1.0.3", 172 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", 173 | "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", 174 | "requires": { 175 | "safe-buffer": "5.1.1" 176 | } 177 | }, 178 | "util-deprecate": { 179 | "version": "1.0.2", 180 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 181 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "goodreads-json-api", 3 | "version": "1.0.4", 4 | "description": "Library to get Goodreads API response in JSON format", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "npm test", 8 | "demo": "node demo/demo.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/myke11j/goodreads-json-api.git" 13 | }, 14 | "keywords": [ 15 | "goodreads", 16 | "api", 17 | "json" 18 | ], 19 | "author": "Mukul <@mukul1904>", 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/myke11j/goodreads-json-api/issues" 23 | }, 24 | "homepage": "https://github.com/myke11j/goodreads-json-api#readme", 25 | "dependencies": { 26 | "async": "^2.6.0", 27 | "cheerio": "^1.0.0-rc.2" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /transformer/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Mukul <@mukul1904) 3 | */ 4 | 5 | 'use strict' 6 | 7 | const mappings = require('./mappings'); 8 | 9 | const transformer = {}; 10 | 11 | transformer.mappings = mappings; 12 | 13 | module.exports = transformer; -------------------------------------------------------------------------------- /transformer/mappings.js: -------------------------------------------------------------------------------- 1 | 'use stinct'; 2 | 3 | module.exports = [ 4 | /** 5 | * Request element mapping 6 | */ 7 | { 8 | 'type': 'Request', 9 | 'mapping': [ 10 | { 11 | 'key': 'key', 12 | 'jsonKey': 'key', 13 | 'helper': 'stripCDATA' 14 | }, 15 | { 16 | 'key': 'authentication', 17 | 'jsonKey': 'authentication' 18 | }, 19 | { 20 | 'key': 'method', 21 | 'jsonKey': 'method', 22 | 'helper': 'stripCDATA' 23 | } 24 | ] 25 | }, 26 | 27 | /** 28 | * work-ids element mapping 29 | */ 30 | { 31 | 'type': 'work-ids', 32 | 'mapping': [ 33 | { 34 | 'key': 'item', 35 | 'jsonKey': 'id' 36 | } 37 | ] 38 | }, 39 | 40 | /** 41 | * book element mapping 42 | */ 43 | { 44 | 'type': 'book', 45 | 'mapping': [ 46 | { 47 | 'key': 'isbn', 48 | 'jsonKey': 'isbn', 49 | 'helper': 'stripCDATA' 50 | }, 51 | { 52 | 'key': 'id', 53 | 'jsonKey': 'goodreads_id' 54 | }, 55 | { 56 | 'key': 'isbn13', 57 | 'jsonKey': 'isbn13', 58 | 'helper': 'stripCDATA' 59 | }, 60 | { 61 | 'key': 'title', 62 | 'jsonKey': 'title', 63 | 'helper': 'newStripCDATA' 64 | }, 65 | { 66 | 'key': 'url', 67 | 'jsonKey': 'url', 68 | 'helper': 'stripCDATA' 69 | }, 70 | { 71 | 'key': 'small_image_url', 72 | 'jsonKey': 'small_image_url' 73 | }, 74 | { 75 | 'key': 'num_pages', 76 | 'jsonKey': 'num_pages', 77 | 'helper': 'stripCDATA' 78 | }, 79 | { 80 | 'key': 'image_url', 81 | 'jsonKey': 'image_url' 82 | }, 83 | { 84 | 'key': 'link', 85 | 'jsonKey': 'link', 86 | 'helper': 'stripCDATA' 87 | }, 88 | { 89 | 'key': 'format', 90 | 'jsonKey': 'format', 91 | 'helper': 'stripCDATA' 92 | }, 93 | { 94 | 'key': 'publisher', 95 | 'jsonKey': 'publisher' 96 | }, 97 | { 98 | 'key': 'publication_day', 99 | 'jsonKey': 'publication_day' 100 | }, 101 | { 102 | 'key': 'publication_year', 103 | 'jsonKey': 'publication_year' 104 | }, 105 | { 106 | 'key': 'publication_month', 107 | 'jsonKey': 'publication_month' 108 | }, 109 | { 110 | 'key': 'average_rating', 111 | 'jsonKey': 'average_rating' 112 | }, 113 | { 114 | 'key': 'ratings_count', 115 | 'jsonKey': 'ratings_count' 116 | }, 117 | { 118 | 'key': 'description', 119 | 'jsonKey': 'description', 120 | 'helper': 'stripHTML' 121 | }, 122 | ] 123 | }, 124 | 125 | /** 126 | * author element mapping 127 | */ 128 | { 129 | 'type': 'author', 130 | 'mapping': [ 131 | { 132 | 'key': 'id', 133 | 'jsonKey': 'author_id' 134 | }, 135 | { 136 | 'key': 'name', 137 | 'jsonKey': 'name' 138 | }, 139 | { 140 | 'key': 'average_rating', 141 | 'jsonKey': 'average_rating' 142 | }, 143 | { 144 | 'key': 'ratings_count', 145 | 'jsonKey': 'ratings_count' 146 | }, 147 | ] 148 | }, 149 | 150 | /** 151 | * popular_shelves element mapping 152 | */ 153 | { 154 | 'type': 'popular_shelves', 155 | 'mapping': [ 156 | { 157 | 'key': 'shelf', 158 | 'jsonKey': 'shelfList', 159 | 'helper': 'shelf' 160 | }, 161 | ] 162 | }, 163 | 164 | /** 165 | * shelf element mapping 166 | */ 167 | { 168 | 'type': 'shelf', 169 | 'mapping': [ 170 | { 171 | 'key': 'name', 172 | 'jsonKey': 'name' 173 | }, 174 | { 175 | 'key': 'count', 176 | 'jsonKey': 'count' 177 | } 178 | ] 179 | }, 180 | 181 | /** 182 | * similar_books element mapping 183 | */ 184 | { 185 | 'type': 'similar_books', 186 | 'mapping': [ 187 | { 188 | 'key': 'book', 189 | 'jsonKey': 'book', 190 | 'transformer': 'book' 191 | } 192 | ] 193 | }, 194 | ]; --------------------------------------------------------------------------------