Skip to content

Commit b7146d1

Browse files
authored
Merge pull request #12 from mcollina/$ref-support
$ref support
2 parents 4c7eaf0 + 1fc7d5a commit b7146d1

File tree

4 files changed

+431
-22
lines changed

4 files changed

+431
-22
lines changed

README.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ fast-json-stringify obj x 5,085,148 ops/sec ±1.56% (89 runs sampled)
2626
- <a href="#missingFields">`Missing fields`</a>
2727
- <a href="#patternProperties">`Pattern Properties`</a>
2828
- <a href="#additionalProperties">`Additional Properties`</a>
29+
- <a href="#ref">`Reuse - $ref`</a>
2930
- <a href="#acknowledgements">`Acknowledgements`</a>
3031
- <a href="#license">`License`</a>
3132

@@ -214,6 +215,85 @@ const obj = {
214215
console.log(stringify(obj)) // '{"matchfoo":"42","otherfoo":"str","matchnum":3,"nomatchstr":"valar morghulis",nomatchint:"313","nickname":"nick"}'
215216
```
216217

218+
<a name="ref"></a>
219+
#### Reuse - $ref
220+
If you want to reuse a definition of a value, you can use the property `$ref`.
221+
The value of `$ref` must be a string in [JSON Pointer](https://tools.ietf.org/html/rfc6901) format.
222+
Example:
223+
```javascript
224+
const schema = {
225+
title: 'Example Schema',
226+
definitions: {
227+
num: {
228+
type: 'object',
229+
properties: {
230+
int: {
231+
type: 'integer'
232+
}
233+
}
234+
},
235+
str: {
236+
type: 'string'
237+
}
238+
},
239+
type: 'object',
240+
properties: {
241+
nickname: {
242+
$ref: '#/definitions/str'
243+
}
244+
},
245+
patternProperties: {
246+
'num': {
247+
$ref: '#/definitions/num'
248+
}
249+
},
250+
additionalProperties: {
251+
$ref: '#/definitions/def'
252+
}
253+
}
254+
255+
const stringify = fastJson(schema)
256+
```
257+
If you need to use an external definition, you can pass it as an option to `fast-json-stringify`.
258+
Example:
259+
```javascript
260+
const schema = {
261+
title: 'Example Schema',
262+
type: 'object',
263+
properties: {
264+
nickname: {
265+
$ref: 'strings#/definitions/str'
266+
}
267+
},
268+
patternProperties: {
269+
'num': {
270+
$ref: 'numbers#/definitions/num'
271+
}
272+
},
273+
additionalProperties: {
274+
$ref: 'strings#/definitions/def'
275+
}
276+
}
277+
278+
const externalSchema = {
279+
numbers: {
280+
definitions: {
281+
num: {
282+
type: 'object',
283+
properties: {
284+
int: {
285+
type: 'integer'
286+
}
287+
}
288+
}
289+
}
290+
},
291+
strings: require('./string-def.json')
292+
}
293+
294+
const stringify = fastJson(schema, { schema: externalSchema })
295+
```
296+
217297
<a name="acknowledgements"></a>
218298
## Acknowledgements
219299

index.js

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
const fastSafeStringify = require('fast-safe-stringify')
44

5-
function build (schema) {
5+
function build (schema, options) {
6+
options = options || {}
67
/* eslint no-new-func: "off" */
78
var code = `
89
'use strict'
@@ -26,7 +27,7 @@ function build (schema) {
2627
switch (schema.type) {
2728
case 'object':
2829
main = '$main'
29-
code = buildObject(schema, code, main)
30+
code = buildObject(schema, code, main, options.schema)
3031
break
3132
case 'string':
3233
main = $asString.name
@@ -43,7 +44,7 @@ function build (schema) {
4344
break
4445
case 'array':
4546
main = '$main'
46-
code = buildArray(schema, code, main)
47+
code = buildArray(schema, code, main, options.schema)
4748
break
4849
default:
4950
throw new Error(`${schema.type} unsupported`)
@@ -141,24 +142,28 @@ function $asRegExp (reg) {
141142
return '"' + reg + '"'
142143
}
143144

144-
function addPatternProperties (pp, ap) {
145+
function addPatternProperties (schema, externalSchema) {
146+
var pp = schema.patternProperties
145147
let code = `
146148
var keys = Object.keys(obj)
147149
for (var i = 0; i < keys.length; i++) {
148150
if (properties[keys[i]]) continue
149151
`
150152
Object.keys(pp).forEach((regex, index) => {
153+
if (pp[regex]['$ref']) {
154+
pp[regex] = refFinder(pp[regex]['$ref'], schema, externalSchema)
155+
}
151156
var type = pp[regex].type
152157
code += `
153158
if (/${regex}/.test(keys[i])) {
154159
`
155160
if (type === 'object') {
156-
code += buildObject(pp[regex], '', 'buildObjectPP' + index)
161+
code += buildObject(pp[regex], '', 'buildObjectPP' + index, externalSchema)
157162
code += `
158163
json += $asString(keys[i]) + ':' + buildObjectPP${index}(obj[keys[i]]) + ','
159164
`
160165
} else if (type === 'array') {
161-
code += buildArray(pp[regex], '', 'buildArrayPP' + index)
166+
code += buildArray(pp[regex], '', 'buildArrayPP' + index, externalSchema)
162167
code += `
163168
json += $asString(keys[i]) + ':' + buildArrayPP${index}(obj[keys[i]]) + ','
164169
`
@@ -189,8 +194,8 @@ function addPatternProperties (pp, ap) {
189194
}
190195
`
191196
})
192-
if (ap) {
193-
code += additionalProperty(ap)
197+
if (schema.additionalProperties) {
198+
code += additionalProperty(schema, externalSchema)
194199
}
195200

196201
code += `
@@ -199,21 +204,26 @@ function addPatternProperties (pp, ap) {
199204
return code
200205
}
201206

202-
function additionalProperty (ap) {
207+
function additionalProperty (schema, externalSchema) {
208+
var ap = schema.additionalProperties
203209
let code = ''
204210
if (ap === true) {
205211
return `
206212
json += $asString(keys[i]) + ':' + fastSafeStringify(obj[keys[i]]) + ','
207213
`
208214
}
215+
if (ap['$ref']) {
216+
ap = refFinder(ap['$ref'], schema, externalSchema)
217+
}
218+
209219
let type = ap.type
210220
if (type === 'object') {
211-
code += buildObject(ap, '', 'buildObjectAP')
221+
code += buildObject(ap, '', 'buildObjectAP', externalSchema)
212222
code += `
213223
json += $asString(keys[i]) + ':' + buildObjectAP(obj[keys[i]]) + ','
214224
`
215225
} else if (type === 'array') {
216-
code += buildArray(ap, '', 'buildArrayAP')
226+
code += buildArray(ap, '', 'buildArrayAP', externalSchema)
217227
code += `
218228
json += $asString(keys[i]) + ':' + buildArrayAP(obj[keys[i]]) + ','
219229
`
@@ -241,26 +251,41 @@ function additionalProperty (ap) {
241251
return code
242252
}
243253

244-
function addAdditionalProperties (ap) {
254+
function addAdditionalProperties (schema, externalSchema) {
245255
return `
246256
var keys = Object.keys(obj)
247257
for (var i = 0; i < keys.length; i++) {
248258
if (properties[keys[i]]) continue
249-
${additionalProperty(ap)}
259+
${additionalProperty(schema, externalSchema)}
250260
}
251261
`
252262
}
253263

254-
function buildObject (schema, code, name) {
264+
function refFinder (ref, schema, externalSchema) {
265+
// Split file from walk
266+
ref = ref.split('#')
267+
// If external file
268+
if (ref[0]) {
269+
schema = externalSchema[ref[0]]
270+
}
271+
const walk = ref[1].split('/')
272+
let code = 'return schema'
273+
for (let i = 1; i < walk.length; i++) {
274+
code += `['${walk[i]}']`
275+
}
276+
return (new Function('schema', code))(schema)
277+
}
278+
279+
function buildObject (schema, code, name, externalSchema) {
255280
code += `
256281
function ${name} (obj) {
257282
var json = '{'
258283
`
259284

260285
if (schema.patternProperties) {
261-
code += addPatternProperties(schema.patternProperties, schema.additionalProperties)
286+
code += addPatternProperties(schema, externalSchema)
262287
} else if (schema.additionalProperties && !schema.patternProperties) {
263-
code += addAdditionalProperties(schema.additionalProperties)
288+
code += addAdditionalProperties(schema, externalSchema)
264289
}
265290

266291
var laterCode = ''
@@ -273,7 +298,11 @@ function buildObject (schema, code, name) {
273298
json += '${$asString(key)}:'
274299
`
275300

276-
const result = nested(laterCode, name, '.' + key, schema.properties[key])
301+
if (schema.properties[key]['$ref']) {
302+
schema.properties[key] = refFinder(schema.properties[key]['$ref'], schema, externalSchema)
303+
}
304+
305+
const result = nested(laterCode, name, '.' + key, schema.properties[key], externalSchema)
277306

278307
code += result.code
279308
laterCode = result.laterCode
@@ -308,15 +337,15 @@ function buildObject (schema, code, name) {
308337
return code
309338
}
310339

311-
function buildArray (schema, code, name) {
340+
function buildArray (schema, code, name, externalSchema) {
312341
code += `
313342
function ${name} (obj) {
314343
var json = '['
315344
`
316345

317346
var laterCode = ''
318347

319-
const result = nested(laterCode, name, '[i]', schema.items)
348+
const result = nested(laterCode, name, '[i]', schema.items, externalSchema)
320349

321350
code += `
322351
const l = obj.length
@@ -342,7 +371,7 @@ function buildArray (schema, code, name) {
342371
return code
343372
}
344373

345-
function nested (laterCode, name, key, schema) {
374+
function nested (laterCode, name, key, schema, externalSchema) {
346375
var code = ''
347376
var funcName
348377
const type = schema.type
@@ -370,14 +399,14 @@ function nested (laterCode, name, key, schema) {
370399
break
371400
case 'object':
372401
funcName = (name + key).replace(/[-.\[\]]/g, '')
373-
laterCode = buildObject(schema, laterCode, funcName)
402+
laterCode = buildObject(schema, laterCode, funcName, externalSchema)
374403
code += `
375404
json += ${funcName}(obj${key})
376405
`
377406
break
378407
case 'array':
379408
funcName = (name + key).replace(/[-.\[\]]/g, '')
380-
laterCode = buildArray(schema, laterCode, funcName)
409+
laterCode = buildArray(schema, laterCode, funcName, externalSchema)
381410
code += `
382411
json += ${funcName}(obj${key})
383412
`

test/ref.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"definitions": {
3+
"def": {
4+
"type": "object",
5+
"properties": {
6+
"str": {
7+
"type": "string"
8+
}
9+
}
10+
}
11+
}
12+
}

0 commit comments

Comments
 (0)