@@ -47,7 +47,7 @@ class Json_DocumentLoaders implements INode {
4747 constructor ( ) {
4848 this . label = 'Json File'
4949 this . name = 'jsonFile'
50- this . version = 3.0
50+ this . version = 3.1
5151 this . type = 'Document'
5252 this . icon = 'json.svg'
5353 this . category = 'Document Loaders'
@@ -66,14 +66,25 @@ class Json_DocumentLoaders implements INode {
6666 type : 'TextSplitter' ,
6767 optional : true
6868 } ,
69+ {
70+ label : 'Separate by JSON Object (JSON Array)' ,
71+ name : 'separateByObject' ,
72+ type : 'boolean' ,
73+ description : 'If enabled and the file is a JSON Array, each JSON object will be extracted as a chunk' ,
74+ optional : true ,
75+ additionalParams : true
76+ } ,
6977 {
7078 label : 'Pointers Extraction (separated by commas)' ,
7179 name : 'pointersName' ,
7280 type : 'string' ,
7381 description :
7482 'Ex: { "key": "value" }, Pointer Extraction = "key", "value" will be extracted as pageContent of the chunk. Use comma to separate multiple pointers' ,
7583 placeholder : 'key1, key2' ,
76- optional : true
84+ optional : true ,
85+ hide : {
86+ separateByObject : true
87+ }
7788 } ,
7889 {
7990 label : 'Additional Metadata' ,
@@ -122,6 +133,7 @@ class Json_DocumentLoaders implements INode {
122133 const pointersName = nodeData . inputs ?. pointersName as string
123134 const metadata = nodeData . inputs ?. metadata
124135 const _omitMetadataKeys = nodeData . inputs ?. omitMetadataKeys as string
136+ const separateByObject = nodeData . inputs ?. separateByObject as boolean
125137 const output = nodeData . outputs ?. output as string
126138
127139 let omitMetadataKeys : string [ ] = [ ]
@@ -153,7 +165,7 @@ class Json_DocumentLoaders implements INode {
153165 if ( ! file ) continue
154166 const fileData = await getFileFromStorage ( file , orgId , chatflowid )
155167 const blob = new Blob ( [ fileData ] )
156- const loader = new JSONLoader ( blob , pointers . length != 0 ? pointers : undefined , metadata )
168+ const loader = new JSONLoader ( blob , pointers . length != 0 ? pointers : undefined , metadata , separateByObject )
157169
158170 if ( textSplitter ) {
159171 let splittedDocs = await loader . load ( )
@@ -176,7 +188,7 @@ class Json_DocumentLoaders implements INode {
176188 splitDataURI . pop ( )
177189 const bf = Buffer . from ( splitDataURI . pop ( ) || '' , 'base64' )
178190 const blob = new Blob ( [ bf ] )
179- const loader = new JSONLoader ( blob , pointers . length != 0 ? pointers : undefined , metadata )
191+ const loader = new JSONLoader ( blob , pointers . length != 0 ? pointers : undefined , metadata , separateByObject )
180192
181193 if ( textSplitter ) {
182194 let splittedDocs = await loader . load ( )
@@ -306,13 +318,20 @@ class TextLoader extends BaseDocumentLoader {
306318class JSONLoader extends TextLoader {
307319 public pointers : string [ ]
308320 private metadataMapping : Record < string , string >
309-
310- constructor ( filePathOrBlob : string | Blob , pointers : string | string [ ] = [ ] , metadataMapping : Record < string , string > = { } ) {
321+ private separateByObject : boolean
322+
323+ constructor (
324+ filePathOrBlob : string | Blob ,
325+ pointers : string | string [ ] = [ ] ,
326+ metadataMapping : Record < string , string > = { } ,
327+ separateByObject : boolean = false
328+ ) {
311329 super ( filePathOrBlob )
312330 this . pointers = Array . isArray ( pointers ) ? pointers : [ pointers ]
313331 if ( metadataMapping ) {
314332 this . metadataMapping = typeof metadataMapping === 'object' ? metadataMapping : JSON . parse ( metadataMapping )
315333 }
334+ this . separateByObject = separateByObject
316335 }
317336
318337 protected async parse ( raw : string ) : Promise < Document [ ] > {
@@ -323,14 +342,24 @@ class JSONLoader extends TextLoader {
323342 const jsonArray = Array . isArray ( json ) ? json : [ json ]
324343
325344 for ( const item of jsonArray ) {
326- const content = this . extractContent ( item )
327- const metadata = this . extractMetadata ( item )
328-
329- for ( const pageContent of content ) {
330- documents . push ( {
331- pageContent,
332- metadata
333- } )
345+ if ( this . separateByObject ) {
346+ if ( typeof item === 'object' && item !== null && ! Array . isArray ( item ) ) {
347+ const metadata = this . extractMetadata ( item )
348+ const pageContent = this . formatObjectAsKeyValue ( item )
349+ documents . push ( {
350+ pageContent,
351+ metadata
352+ } )
353+ }
354+ } else {
355+ const content = this . extractContent ( item )
356+ const metadata = this . extractMetadata ( item )
357+ for ( const pageContent of content ) {
358+ documents . push ( {
359+ pageContent,
360+ metadata
361+ } )
362+ }
334363 }
335364 }
336365
@@ -370,6 +399,30 @@ class JSONLoader extends TextLoader {
370399 return metadata
371400 }
372401
402+ /**
403+ * Formats a JSON object as readable key-value pairs
404+ */
405+ private formatObjectAsKeyValue ( obj : any , prefix : string = '' ) : string {
406+ const lines : string [ ] = [ ]
407+
408+ for ( const [ key , value ] of Object . entries ( obj ) ) {
409+ const fullKey = prefix ? `${ prefix } .${ key } ` : key
410+
411+ if ( value === null || value === undefined ) {
412+ lines . push ( `${ fullKey } : ${ value } ` )
413+ } else if ( Array . isArray ( value ) ) {
414+ lines . push ( `${ fullKey } : ${ JSON . stringify ( value ) } ` )
415+ } else if ( typeof value === 'object' ) {
416+ // Recursively format nested objects
417+ lines . push ( this . formatObjectAsKeyValue ( value , fullKey ) )
418+ } else {
419+ lines . push ( `${ fullKey } : ${ value } ` )
420+ }
421+ }
422+
423+ return lines . join ( '\n' )
424+ }
425+
373426 /**
374427 * If JSON pointers are specified, return all strings below any of them
375428 * and exclude all other nodes expect if they match a JSON pointer.
0 commit comments