@@ -27,6 +27,8 @@ const validLargeArrayMechanisms = [
2727 'json-stringify'
2828]
2929
30+ const addComma = '!addComma && (addComma = true) || (json += \',\')'
31+
3032let schemaIdCounter = 0
3133
3234const mergedSchemaRef = Symbol ( 'fjs-merged-schema-ref' )
@@ -260,7 +262,7 @@ function inferTypeByKeyword (schema) {
260262 return schema . type
261263}
262264
263- function buildExtraObjectPropertiesSerializer ( context , location , addComma ) {
265+ function buildExtraObjectPropertiesSerializer ( context , location ) {
264266 const schema = location . schema
265267 const propertiesKeys = Object . keys ( schema . properties || { } )
266268
@@ -319,76 +321,88 @@ function buildExtraObjectPropertiesSerializer (context, location, addComma) {
319321}
320322
321323function buildInnerObject ( context , location ) {
324+ let code = ''
322325 const schema = location . schema
326+ const required = schema . required || [ ]
323327
324328 const propertiesLocation = location . getPropertyLocation ( 'properties' )
325- const requiredProperties = schema . required || [ ]
326-
327- // Should serialize required properties first
328- const propertiesKeys = Object . keys ( schema . properties || { } ) . sort (
329- ( key1 , key2 ) => {
330- const required1 = requiredProperties . includes ( key1 )
331- const required2 = requiredProperties . includes ( key2 )
332- return required1 === required2 ? 0 : required1 ? - 1 : 1
333- }
334- )
335- const hasRequiredProperties = requiredProperties . includes ( propertiesKeys [ 0 ] )
336329
337- let code = ''
330+ const requiredWithDefault = [ ]
331+ const requiredWithoutDefault = [ ]
332+ if ( schema . properties ) {
333+ for ( const key of Object . keys ( schema . properties ) ) {
334+ if ( required . indexOf ( key ) === - 1 ) {
335+ continue
336+ }
337+ let propertyLocation = propertiesLocation . getPropertyLocation ( key )
338+ if ( propertyLocation . schema . $ref ) {
339+ propertyLocation = resolveRef ( context , propertyLocation )
340+ }
338341
339- for ( const key of requiredProperties ) {
340- if ( ! propertiesKeys . includes ( key ) ) {
341- code += `if (obj['${ key } '] === undefined) throw new Error('"${ key } " is required!')\n`
342+ const sanitizedKey = JSON . stringify ( key )
343+
344+ // Using obj['key'] !== undefined instead of obj.hasOwnProperty(prop) for perf reasons,
345+ // see https://github.com/mcollina/fast-json-stringify/pull/3 for discussion.
346+ const defaultValue = propertyLocation . schema . default
347+ if ( defaultValue === undefined ) {
348+ code += `if (obj[${ sanitizedKey } ] === undefined) throw new Error('${ sanitizedKey } is required!')\n`
349+ requiredWithoutDefault . push ( key )
350+ }
351+ requiredWithDefault . push ( key )
342352 }
343353 }
344354
345- code += 'let json = \'{\'\n'
346-
347- let addComma = ''
348- if ( ! hasRequiredProperties ) {
349- code += 'let addComma = false\n'
350- addComma = '!addComma && (addComma = true) || (json += \',\')'
355+ // handle extraneous required fields
356+ for ( const requiredProperty of required ) {
357+ if ( requiredWithDefault . indexOf ( requiredProperty ) !== - 1 ) continue
358+ code += `if (obj['${ requiredProperty } '] === undefined) throw new Error('"${ requiredProperty } " is required!')\n`
351359 }
352360
353- for ( const key of propertiesKeys ) {
354- let propertyLocation = propertiesLocation . getPropertyLocation ( key )
355- if ( propertyLocation . schema . $ref ) {
356- propertyLocation = resolveRef ( context , propertyLocation )
357- }
361+ code += `
362+ let addComma = false
363+ let json = '{'
364+ `
358365
359- const sanitizedKey = JSON . stringify ( key )
360- const defaultValue = propertyLocation . schema . default
361- const isRequired = requiredProperties . includes ( key )
366+ if ( schema . properties ) {
367+ for ( const key of Object . keys ( schema . properties ) ) {
368+ let propertyLocation = propertiesLocation . getPropertyLocation ( key )
369+ if ( propertyLocation . schema . $ref ) {
370+ propertyLocation = resolveRef ( context , propertyLocation )
371+ }
362372
363- code += `
364- if (obj[${ sanitizedKey } ] !== undefined) {
373+ const sanitizedKey = JSON . stringify ( key )
374+
375+ if ( requiredWithoutDefault . indexOf ( key ) !== - 1 ) {
376+ code += `
365377 ${ addComma }
366378 json += ${ JSON . stringify ( sanitizedKey + ':' ) }
367379 ${ buildValue ( context , propertyLocation , `obj[${ sanitizedKey } ]` ) }
368- }`
369-
370- if ( defaultValue !== undefined ) {
371- code += ` else {
372- ${ addComma }
373- json += ${ JSON . stringify ( sanitizedKey + ':' + JSON . stringify ( defaultValue ) ) }
374- }
375380 `
376- } else if ( isRequired ) {
377- code += ` else {
378- throw new Error('${ sanitizedKey } is required!')
381+ } else {
382+ // Using obj['key'] !== undefined instead of obj.hasOwnProperty(prop) for perf reasons,
383+ // see https://github.com/mcollina/fast-json-stringify/pull/3 for discussion.
384+ code += `
385+ if (obj[${ sanitizedKey } ] !== undefined) {
386+ ${ addComma }
387+ json += ${ JSON . stringify ( sanitizedKey + ':' ) }
388+ ${ buildValue ( context , propertyLocation , `obj[${ sanitizedKey } ]` ) }
389+ }
390+ `
391+ const defaultValue = propertyLocation . schema . default
392+ if ( defaultValue !== undefined ) {
393+ code += `
394+ else {
395+ ${ addComma }
396+ json += ${ JSON . stringify ( sanitizedKey + ':' + JSON . stringify ( defaultValue ) ) }
397+ }
398+ `
399+ }
379400 }
380- `
381- } else {
382- code += '\n'
383- }
384-
385- if ( hasRequiredProperties ) {
386- addComma = 'json += \',\''
387401 }
388402 }
389403
390404 if ( schema . patternProperties || schema . additionalProperties ) {
391- code += buildExtraObjectPropertiesSerializer ( context , location , addComma )
405+ code += buildExtraObjectPropertiesSerializer ( context , location )
392406 }
393407
394408 code += `
0 commit comments