Skip to content

Commit

Permalink
Merge pull request #12 from mcollina/$ref-support
Browse files Browse the repository at this point in the history
$ref support
  • Loading branch information
delvedor committed Oct 8, 2016
2 parents 4c7eaf0 + 1fc7d5a commit b7146d1
Show file tree
Hide file tree
Showing 4 changed files with 431 additions and 22 deletions.
80 changes: 80 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ fast-json-stringify obj x 5,085,148 ops/sec ±1.56% (89 runs sampled)
- <a href="#missingFields">`Missing fields`</a>
- <a href="#patternProperties">`Pattern Properties`</a>
- <a href="#additionalProperties">`Additional Properties`</a>
- <a href="#ref">`Reuse - $ref`</a>
- <a href="#acknowledgements">`Acknowledgements`</a>
- <a href="#license">`License`</a>

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

<a name="ref"></a>
#### Reuse - $ref
If you want to reuse a definition of a value, you can use the property `$ref`.
The value of `$ref` must be a string in [JSON Pointer](https://tools.ietf.org/html/rfc6901) format.
Example:
```javascript
const schema = {
title: 'Example Schema',
definitions: {
num: {
type: 'object',
properties: {
int: {
type: 'integer'
}
}
},
str: {
type: 'string'
}
},
type: 'object',
properties: {
nickname: {
$ref: '#/definitions/str'
}
},
patternProperties: {
'num': {
$ref: '#/definitions/num'
}
},
additionalProperties: {
$ref: '#/definitions/def'
}
}

const stringify = fastJson(schema)
```
If you need to use an external definition, you can pass it as an option to `fast-json-stringify`.
Example:
```javascript
const schema = {
title: 'Example Schema',
type: 'object',
properties: {
nickname: {
$ref: 'strings#/definitions/str'
}
},
patternProperties: {
'num': {
$ref: 'numbers#/definitions/num'
}
},
additionalProperties: {
$ref: 'strings#/definitions/def'
}
}

const externalSchema = {
numbers: {
definitions: {
num: {
type: 'object',
properties: {
int: {
type: 'integer'
}
}
}
}
},
strings: require('./string-def.json')
}

const stringify = fastJson(schema, { schema: externalSchema })
```

<a name="acknowledgements"></a>
## Acknowledgements

Expand Down
73 changes: 51 additions & 22 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

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

function build (schema) {
function build (schema, options) {
options = options || {}
/* eslint no-new-func: "off" */
var code = `
'use strict'
Expand All @@ -26,7 +27,7 @@ function build (schema) {
switch (schema.type) {
case 'object':
main = '$main'
code = buildObject(schema, code, main)
code = buildObject(schema, code, main, options.schema)
break
case 'string':
main = $asString.name
Expand All @@ -43,7 +44,7 @@ function build (schema) {
break
case 'array':
main = '$main'
code = buildArray(schema, code, main)
code = buildArray(schema, code, main, options.schema)
break
default:
throw new Error(`${schema.type} unsupported`)
Expand Down Expand Up @@ -141,24 +142,28 @@ function $asRegExp (reg) {
return '"' + reg + '"'
}

function addPatternProperties (pp, ap) {
function addPatternProperties (schema, externalSchema) {
var pp = schema.patternProperties
let code = `
var keys = Object.keys(obj)
for (var i = 0; i < keys.length; i++) {
if (properties[keys[i]]) continue
`
Object.keys(pp).forEach((regex, index) => {
if (pp[regex]['$ref']) {
pp[regex] = refFinder(pp[regex]['$ref'], schema, externalSchema)
}
var type = pp[regex].type
code += `
if (/${regex}/.test(keys[i])) {
`
if (type === 'object') {
code += buildObject(pp[regex], '', 'buildObjectPP' + index)
code += buildObject(pp[regex], '', 'buildObjectPP' + index, externalSchema)
code += `
json += $asString(keys[i]) + ':' + buildObjectPP${index}(obj[keys[i]]) + ','
`
} else if (type === 'array') {
code += buildArray(pp[regex], '', 'buildArrayPP' + index)
code += buildArray(pp[regex], '', 'buildArrayPP' + index, externalSchema)
code += `
json += $asString(keys[i]) + ':' + buildArrayPP${index}(obj[keys[i]]) + ','
`
Expand Down Expand Up @@ -189,8 +194,8 @@ function addPatternProperties (pp, ap) {
}
`
})
if (ap) {
code += additionalProperty(ap)
if (schema.additionalProperties) {
code += additionalProperty(schema, externalSchema)
}

code += `
Expand All @@ -199,21 +204,26 @@ function addPatternProperties (pp, ap) {
return code
}

function additionalProperty (ap) {
function additionalProperty (schema, externalSchema) {
var ap = schema.additionalProperties
let code = ''
if (ap === true) {
return `
json += $asString(keys[i]) + ':' + fastSafeStringify(obj[keys[i]]) + ','
`
}
if (ap['$ref']) {
ap = refFinder(ap['$ref'], schema, externalSchema)
}

let type = ap.type
if (type === 'object') {
code += buildObject(ap, '', 'buildObjectAP')
code += buildObject(ap, '', 'buildObjectAP', externalSchema)
code += `
json += $asString(keys[i]) + ':' + buildObjectAP(obj[keys[i]]) + ','
`
} else if (type === 'array') {
code += buildArray(ap, '', 'buildArrayAP')
code += buildArray(ap, '', 'buildArrayAP', externalSchema)
code += `
json += $asString(keys[i]) + ':' + buildArrayAP(obj[keys[i]]) + ','
`
Expand Down Expand Up @@ -241,26 +251,41 @@ function additionalProperty (ap) {
return code
}

function addAdditionalProperties (ap) {
function addAdditionalProperties (schema, externalSchema) {
return `
var keys = Object.keys(obj)
for (var i = 0; i < keys.length; i++) {
if (properties[keys[i]]) continue
${additionalProperty(ap)}
${additionalProperty(schema, externalSchema)}
}
`
}

function buildObject (schema, code, name) {
function refFinder (ref, schema, externalSchema) {
// Split file from walk
ref = ref.split('#')
// If external file
if (ref[0]) {
schema = externalSchema[ref[0]]
}
const walk = ref[1].split('/')
let code = 'return schema'
for (let i = 1; i < walk.length; i++) {
code += `['${walk[i]}']`
}
return (new Function('schema', code))(schema)
}

function buildObject (schema, code, name, externalSchema) {
code += `
function ${name} (obj) {
var json = '{'
`

if (schema.patternProperties) {
code += addPatternProperties(schema.patternProperties, schema.additionalProperties)
code += addPatternProperties(schema, externalSchema)
} else if (schema.additionalProperties && !schema.patternProperties) {
code += addAdditionalProperties(schema.additionalProperties)
code += addAdditionalProperties(schema, externalSchema)
}

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

const result = nested(laterCode, name, '.' + key, schema.properties[key])
if (schema.properties[key]['$ref']) {
schema.properties[key] = refFinder(schema.properties[key]['$ref'], schema, externalSchema)
}

const result = nested(laterCode, name, '.' + key, schema.properties[key], externalSchema)

code += result.code
laterCode = result.laterCode
Expand Down Expand Up @@ -308,15 +337,15 @@ function buildObject (schema, code, name) {
return code
}

function buildArray (schema, code, name) {
function buildArray (schema, code, name, externalSchema) {
code += `
function ${name} (obj) {
var json = '['
`

var laterCode = ''

const result = nested(laterCode, name, '[i]', schema.items)
const result = nested(laterCode, name, '[i]', schema.items, externalSchema)

code += `
const l = obj.length
Expand All @@ -342,7 +371,7 @@ function buildArray (schema, code, name) {
return code
}

function nested (laterCode, name, key, schema) {
function nested (laterCode, name, key, schema, externalSchema) {
var code = ''
var funcName
const type = schema.type
Expand Down Expand Up @@ -370,14 +399,14 @@ function nested (laterCode, name, key, schema) {
break
case 'object':
funcName = (name + key).replace(/[-.\[\]]/g, '')
laterCode = buildObject(schema, laterCode, funcName)
laterCode = buildObject(schema, laterCode, funcName, externalSchema)
code += `
json += ${funcName}(obj${key})
`
break
case 'array':
funcName = (name + key).replace(/[-.\[\]]/g, '')
laterCode = buildArray(schema, laterCode, funcName)
laterCode = buildArray(schema, laterCode, funcName, externalSchema)
code += `
json += ${funcName}(obj${key})
`
Expand Down
12 changes: 12 additions & 0 deletions test/ref.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"definitions": {
"def": {
"type": "object",
"properties": {
"str": {
"type": "string"
}
}
}
}
}
Loading

0 comments on commit b7146d1

Please sign in to comment.