Search code examples
javascriptarraysjsonobjectstringify

JSON.stringify not working with nested array of objects


I know this question has been asked but none of the solutions are working for me and I can't figure out what's wrong. I have an object with a nested array of objects I am stringifying but I get a blank array of the nested array when I use JSON.stringify on it.

This is a simplified version of the way I'm constructing the object. The main difference is that there is a for loop iterating through all the rows, here I am just manually creating 2 rows

// JAVASCRIPT
let obj = {};
obj['type'] = 'Setting';
obj['id'] = 1;
obj['import'] = parseCSV();

function parseCSV() {
    let jsonData = [];
    let row1 = {};
    let row2 = {};
    row1['date'] = '2022-01-01';
    row1['amount'] = '30';
    row2['date'] = '2022-01-02';
    row2['amount'] = '50';
    jsonData.push(row1);
    jsonData.push(row2);
    return jsonData;
}

console.log('RAW DATA', obj);
console.log('STRINGIFIED', JSON.stringify(obj));

The above outputs the correct stringified JSON

Stringified Working

But the full version of my code gives me a blank array for import.

Full Object

Stringified Not Working

Both objects look identical to me. The culprit is somewhere in my parseCSV function, because when I use the simplified version above I get the correct stringified data, but I can't pinpoint where I'm wrong. Below is my full function.

function parseCSV(file) {
    let filename = file.name;
    let extension = filename.substring(filename.lastIndexOf('.')).toUpperCase();
    if(extension == '.CSV') {
        try {
            let reader = new FileReader();
            let jsonData = [];
            let headers = [];
            reader.readAsBinaryString(file);
            reader.onload = function(e) {
                let rows = e.target.result.split('\n');
                for(let i = 0; i < rows.length; i++) {
                    let cells = rows[i].split(',');
                    let rowData = {};
                    for(let j = 0; j < cells.length; j++) {
                        if(i == 0) headers.push(cells[j].trim());
                        else {
                            if(headers[j]) rowData[headers[j]] = cells[j].trim();
                        }
                    }
                    if(i != 0 && rowData['date'] != '') jsonData.push(rowData);
                }
            }
            return jsonData;
        } catch(err) {
            console.error('!! ERROR READING CSV FILE', err);
        }
    } else alert('PLEASE UPLOAD A VALID CSV FILE');*/
}

Thanks for the help!

EDIT

When I add await before parseCSV as @BJRINT's answer suggests I get a syntax error await is only valid in async function

async function submitForm(event) {
    event.preventDefault();
    
    let newItem = await gatherFormData(event.target);
    
    return fetch('server.php', {
        method: "POST",
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json'
        },
        body: JSON.stringify(newItem)
    })
    .then(checkError)
    .then(data => parseData(data))
    .catch(err => console.error('>> ERROR READING JSON DATA', err));
}

function gatherFormData(target) {
    const inputs = target.querySelectorAll('input');
    let obj = {};
    inputs.forEach(function(input) {
        if(intKeys.indexOf(input.name) >= 0) obj[input.name] = parseInt(input.value);
        else if(curKeys.indexOf(input.name) >= 0) obj[input.name] = parseInt(parseFloat(input.value) * 100);
        else if(chkKeys.indexOf(input.name) >= 0) input.checked ? obj[input.name] = 1 : obj[input.name] = 0;
        else if(fileKeys.indexOf(input.name) >= 0 && input.files.length > 0) obj[input.name] = parseCSV(input.files[0]);
        else obj[input.name] = input.value;
    });
    return obj;
}


Solution

  • The solution that worked for my specific case was to use a combination of BJRINT's answer and a timer to keep checking if the data had finished loading which I found here.

    async function parseCSV(file) {
        return await new Promise((resolve, reject) => {
            let extension = file.name.substring(file.name.lastIndexOf('.')).toUpperCase();
            if(extension !== '.CSV') reject('PLEASE UPLOAD A VALID CSV FILE');
            
            try {
                let reader = new FileReader();
                reader.readAsText(file);
                reader.onload = function(e) {
                    let jsonData = [];
                    let headers = [];
                    let rows = e.target.result.split(/\r\n|\r|\n/);
                    for(let i = 0; i < rows.length; i++) {
                        let cells = rows[i].split(',');
                        let rowData = {};
                        for(let j = 0; j < cells.length; j++) {
                            if(i == 0) headers.push(cells[j].trim());
                            else {
                                if(headers[j]) rowData[headers[j]] = cells[j].trim();
                            }
                        }
                        if(i != 0 && rowData['date'] != '') jsonData.push(rowData);
                    }
                    resolve(jsonData);
                }
            } catch(err) {
                reject(err);
            }
        });
    }
    
    function submitForm(event) {
        event.preventDefault();
        showForm(false);
        loading.classList.remove('hidden');
        
        let ready = true;
        const inputs = event.target.querySelectorAll('input');
        let newItem = {};
        
        let check = function() {
            if(ready === true) {
                console.log(newItem);
                console.log(JSON.stringify(newItem));
                
                return fetch('server.php', {
                    method: "POST",
                    headers: {
                        'Content-Type': 'application/json',
                        'Accept': 'application/json'
                    },
                    body: JSON.stringify(newItem)
                })
                .then(checkError)
                .then(data => parseData(data))
                .catch(err => console.error('>> ERROR READING JSON DATA', err));
            }
            setTimeout(check, 1000);
        }
        
        inputs.forEach(function(input) {
            if(intKeys.indexOf(input.name) >= 0) newItem[input.name] = parseInt(input.value);
            else if(curKeys.indexOf(input.name) >= 0) newItem[input.name] = parseInt(parseFloat(input.value) * 100);
            else if(chkKeys.indexOf(input.name) >= 0) input.checked ? newItem[input.name] = 1 : newItem[input.name] = 0;
            else if(fileKeys.indexOf(input.name) >= 0 && input.files.length > 0) {
                ready = false;
                parseCSV(input.files[0]).then(data => {
                    ready = true;
                    newItem[input.name] = data;
                });
            }
            else newItem[input.name] = input.value;
        });
        
        check();
    }