diff --git a/README.md b/README.md
index f3a0d206..0439973e 100644
--- a/README.md
+++ b/README.md
@@ -118,7 +118,8 @@ const fastJson = require('fast-json-stringify')
const stringify = fastJson(mySchema, {
schema: { ... },
ajv: { ... },
- rounding: 'ceil'
+ rounding: 'ceil',
+ enableStream: false
})
```
@@ -127,6 +128,7 @@ const stringify = fastJson(mySchema, {
- `rounding`: setup how the `integer` types will be rounded when not integers. [More details](#integer)
- `largeArrayMechanism`: set the mechanism that should be used to handle large
(by default `20000` or more items) arrays. [More details](#largearrays)
+- `enableStream`: write the json on a stream [More details](#stream)
@@ -648,6 +650,37 @@ const stringify = fastJson({
})
```
+
+#### stream
+You can set specific a stream to write
+Example:
+```javascript
+const fastJson = require('fast-json-stringify')
+
+const schema = {
+ title: 'Example Schema',
+ type: 'object',
+ properties: {
+ foo: {
+ type: 'string'
+ }
+}
+const stringify = fastJson(schema, { enableStream: true })
+
+const data = { foo: 'bar' }
+const stream = new Readable()
+stringify(data, stream);
+
+const chunks = []
+stream.on('data', (chunk) => {
+ const str = Buffer.from(chunk).toString('utf8')
+ console.log('chunk', str)
+ chunks.push(str)
+})
+stream.on('end', () => console.log(chunks.join('')))
+```
+
+
##### Benchmarks
For reference, here goes some benchmarks for comparison over the three
diff --git a/index.js b/index.js
index 9bb50116..71703c34 100644
--- a/index.js
+++ b/index.js
@@ -155,9 +155,14 @@ function build (schema, options) {
`
} else {
contextFunctionCode = `
- function main (input) {
+ let stream
+ function main (input, _stream) {
+ stream = _stream
let json = ''
${code}
+ if( stream ){
+ stream.push(null)
+ }
return json
}
${context.functions.join('\n')}
@@ -263,7 +268,7 @@ function buildExtraObjectPropertiesSerializer (context, location, addComma) {
const schema = location.schema
const propertiesKeys = Object.keys(schema.properties || {})
- let code = `
+ let code = `/** begin buildExtraObjectPropertiesSerializer */
const propertiesKeys = ${JSON.stringify(propertiesKeys)}
for (const [key, value] of Object.entries(obj)) {
if (
@@ -313,6 +318,8 @@ function buildExtraObjectPropertiesSerializer (context, location, addComma) {
code += `
}
+
+ /** end buildExtraObjectPropertiesSerializer */
`
return code
}
@@ -333,7 +340,9 @@ function buildInnerObject (context, location) {
)
const hasRequiredProperties = requiredProperties.includes(propertiesKeys[0])
- let code = 'let value\n'
+ let code = `/** begin buildInnerObject */
+ let value
+ `
for (const key of requiredProperties) {
if (!propertiesKeys.includes(key)) {
@@ -363,9 +372,16 @@ function buildInnerObject (context, location) {
value = obj[${sanitizedKey}]
if (value !== undefined) {
${addComma}
- json += ${JSON.stringify(sanitizedKey + ':')}
- ${buildValue(context, propertyLocation, 'value')}
- }`
+ json += ${JSON.stringify(sanitizedKey + ':')}`
+ if (context.options.enableStream) {
+ code += `
+ stream.push(json)
+ json = '';
+ `
+ }
+ code += `
+ ${buildValue(context, propertyLocation, 'value')}
+ }`
if (defaultValue !== undefined) {
code += ` else {
@@ -392,7 +408,21 @@ function buildInnerObject (context, location) {
}
code += `
- return json + '}'
+ json += '}'
+ `
+
+ if (context.options.enableStream) {
+ code += `
+ if( json ){
+ stream.push(json)
+ json = ''
+ }
+ `
+ }
+
+ code += `
+ return json
+ /** end buildInnerObject */
`
return code
}
@@ -476,6 +506,7 @@ function buildObject (context, location) {
}
let functionCode = `
+ /** begin buildObject */
`
const nullable = schema.nullable === true
@@ -487,6 +518,8 @@ function buildObject (context, location) {
${buildInnerObject(context, location)}
}
+
+ /** end buildObject */
`
context.functions.push(functionCode)
@@ -518,13 +551,15 @@ function buildArray (context, location) {
}
let functionCode = `
+ /** begin buildArray */
+
function ${functionName} (obj) {
// ${schemaRef}
`
const nullable = schema.nullable === true
functionCode += `
- ${!nullable ? 'if (obj === null) return \'[]\'' : ''}
+ ${!nullable ? `if (obj === null) return ${context.options.enableStream ? 'stream.push(\'[]\')' : '\'[]\''}` : ''}
if (!Array.isArray(obj)) {
throw new TypeError(\`The value of '${schemaRef}' does not match schema definition.\`)
}
@@ -540,7 +575,13 @@ function buildArray (context, location) {
}
if (largeArrayMechanism === 'json-stringify') {
- functionCode += `if (arrayLength && arrayLength >= ${largeArraySize}) return JSON.stringify(obj)\n`
+ functionCode += `if (arrayLength && arrayLength >= ${largeArraySize}) return ${context.options.enableStream ? 'stream.push(JSON.stringify(obj))' : 'JSON.stringify(obj)'}\n`
+ }
+
+ if (context.options.enableStream) {
+ functionCode += `
+ stream.push('[')
+ `
}
functionCode += `
@@ -548,11 +589,22 @@ function buildArray (context, location) {
let jsonOutput = ''
`
+ let codePushLoop = ''
+ if (context.options.enableStream) {
+ codePushLoop = `
+ if( jsonOutput ){
+ stream.push(jsonOutput)
+ jsonOutput = ''
+ }
+ `
+ }
+
if (Array.isArray(itemsSchema)) {
for (let i = 0; i < itemsSchema.length; i++) {
const item = itemsSchema[i]
functionCode += `value = obj[${i}]`
const tmpRes = buildValue(context, itemsLocation.getPropertyLocation(i), 'value')
+
functionCode += `
if (${i} < arrayLength) {
if (${buildArrayTypeCondition(item.type, `[${i}]`)}) {
@@ -576,6 +628,7 @@ function buildArray (context, location) {
if (i < arrayLength - 1) {
jsonOutput += ','
}
+ ${codePushLoop}
}`
}
} else {
@@ -588,12 +641,25 @@ function buildArray (context, location) {
if (i < arrayLength - 1) {
jsonOutput += ','
}
+ ${codePushLoop}
}`
}
+ if (context.options.enableStream) {
+ functionCode += `
+ stream.push(']')
+ return ''
+ `
+ } else {
+ functionCode += `
+ return \`[\${jsonOutput}]\`
+ `
+ }
+
functionCode += `
- return \`[\${jsonOutput}]\`
- }`
+ }
+ /** end buildArray */
+ `
context.functions.push(functionCode)
return functionName
@@ -650,7 +716,9 @@ function buildMultiTypeSerializer (context, location, input) {
const schema = location.schema
const types = schema.type.sort(t1 => t1 === 'null' ? -1 : 1)
- let code = ''
+ let code = `
+ /** begin buildMultiTypeSerializer */
+ `
types.forEach((type, index) => {
location.schema = { ...location.schema, type }
@@ -710,6 +778,8 @@ function buildMultiTypeSerializer (context, location, input) {
}
code += `
else throw new TypeError(\`The value of '${schemaRef}' does not match schema definition.\`)
+
+ /** end buildMultiTypeSerializer */
`
return code
@@ -980,7 +1050,7 @@ function buildValue (context, location, input) {
}
}
- let code = ''
+ let code = '\n/** begin buildValue */\n'
const type = schema.type
const nullable = schema.nullable === true
@@ -993,11 +1063,17 @@ function buildValue (context, location, input) {
}
if (schema.const !== undefined) {
+ code += '\n/** begin buildConstSerializer */\n'
code += buildConstSerializer(location, input)
+ code += '\n/** end buildConstSerializer */\n'
} else if (Array.isArray(type)) {
+ code += '\n/** begin buildMultiTypeSerializer */\n'
code += buildMultiTypeSerializer(context, location, input)
+ code += '\n/** end buildMultiTypeSerializer */\n'
} else {
+ code += '\n/** begin buildSingleTypeSerializer */\n'
code += buildSingleTypeSerializer(context, location, input)
+ code += '\n/** end buildSingleTypeSerializer */\n'
}
if (nullable) {
@@ -1005,7 +1081,7 @@ function buildValue (context, location, input) {
}
`
}
-
+ code += '\n/** end buildValue */\n'
return code
}