15

I am trying to create a JSON file from the data received from a CSV file uploaded using a file uploader input.

I have found lots of posts doing this in Javascript but they just aren't quite working for me in Typescript.

The error I get when running the below code is csv.Split is not a function, does anyone have any ideas how I can alter my code to work.

Let me know if you need more information and Thanks in advance.

component.ts

public testFile() {
    var file = (<HTMLInputElement>document.getElementById('fileInput')).files[0];        

    var jsonFile = this.csvJSON(file);


    // Set Http POST options
    let headers = new Headers({ 'Content-Type': 'application/json' });
    let options = new RequestOptions({ headers: headers });

    // Call Api with test connection data 
    this.http
        .post('/api/TestConnection/TestConnection', jsonFile, options)
        .subscribe(data => {
            // alert request ok
            alert('ok');
        }, error => {
            // Log error
            console.log(error.json());
        });
}

public csvJSON(csv) {
    var lines = csv.split("\n");

    var result = [];

    var headers = lines[0].split(",");

    for (var i = 1; i < lines.length; i++) {

        var obj = {};
        var currentline = lines[i].split(",");

        for (var j = 0; j < headers.length; j++) {
            obj[headers[j]] = currentline[j];
        }

        result.push(obj);

    }

    //return result; //JavaScript object
    return JSON.stringify(result); //JSON
}
5
  • Isn't it easier and more readable if you pour data from csv to Hash Table(array) and then convert it to json? Commented Mar 15, 2017 at 12:05
  • There definitely could be a better way to do it, exactly why I am asking for advice. Commented Mar 15, 2017 at 12:08
  • Okey, what errors do you get from your current code? Commented Mar 15, 2017 at 13:33
  • the fact that csv.split doesn't exist. Commented Mar 15, 2017 at 14:23
  • You should use FileReader.readAsText to read file and then send it to csvJSON method Commented Mar 15, 2017 at 14:30

4 Answers 4

15

You are passing File to csvJSON method instead of file's text. You can use FileReader to read its content. Here's an example

const convertFile = () => {
  const input = document.getElementById('fileInput');

  const reader = new FileReader();
  reader.onload = () => {
    let text = reader.result;
    console.log('CSV: ', text.substring(0, 100) + '...');
    
    //convert text to json here
    //var json = this.csvJSON(text);
  };
  reader.readAsText(input.files[0]);
};
<input type='file' onchange='convertFile(event)' id='fileInput'>

Sign up to request clarification or add additional context in comments.

Comments

5

Here is my work on CSV to JSON worked perfectly.

Stackbliz Demo

contact-imports.component.ts

import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
import { ToastrService } from 'ngx-toastr';

@Component({
  selector: 'app-contact-imports',
  templateUrl: './contact-imports.component.html',
  styleUrls: ['./contact-imports.component.scss']
})


export class ContactImportsComponent implements OnInit {

  csvContent: string;
  contacts: Array<any> = [];
  properties:any = "";
  flag:boolean = false;
  constructor( private toastr: ToastrService) { }
  ngOnInit() {
    
  }

  
  onFileLoad(fileLoadedEvent) {
    const textFromFileLoaded = fileLoadedEvent.target.result;
    this.csvContent = textFromFileLoaded;

    //Flag is for extracting first line
    let flag = false;
    // Main Data
    let objarray: Array<any> = [];
    //Properties
    let prop: Array<any> = [];
    //Total Length
    let size: any = 0;

    for (const line of this.csvContent.split(/[\r\n]+/)) {

      if (flag) {

        let obj = {};
        for (let k = 0; k < size; k++) {
          //Dynamic Object Properties
          obj[prop[k]] = line.split(',')[k]
        }
        objarray.push(obj);

      } else {
        //First Line of CSV will be having Properties
        for (let k = 0; k < line.split(',').length; k++) {
          size = line.split(',').length;
          //Removing all the spaces to make them usefull, also removing any " characters 
          prop.push(line.split(',')[k].replace(/ /g, '').replace(/"/g, ""));
        }
        flag = true;
      }
    }
    this.contacts = objarray;
    this.properties = [];
  
    this.properties = prop;
    console.log(this.properties);
    console.log(this.contacts);
    this.flag = true;
  

    // console.log(this.csvContent);
  }




  onFileSelect(input: HTMLInputElement) {

    const files = input.files;
    var fileTypes = ['csv'];  //acceptable file types

    if (files && files.length) {
      var extension = input.files[0].name.split('.').pop().toLowerCase(),  //file extension from input file
      isSuccess = fileTypes.indexOf(extension) > -1;  //is extension in acceptable types
       //console.log(isSuccess);
      //  console.log("Filename: " + files[0].name);
      // console.log("Type: " + files[0].type);
      //  console.log("Size: " + files[0].size + " bytes");
      if(isSuccess){
        const fileToRead = files[0];

        const fileReader = new FileReader();
        fileReader.onload = this.onFileLoad;
  
  
        fileReader.readAsText(fileToRead, "UTF-8");
      }else{
        this.toastr.error("Invalid File Type", 'Failed');
      }

    
    }

  }
}

contact-imports.component.html

 <div class="container-fluid">
      <div class="col-md-6">
          <img src="https://img.icons8.com/color/48/000000/csv.png"/> 
          <span class="text-muted" style="font-size: 22px;">Import Contacts From CSV</span>
        
     
          <div class="form-group">
                 <input class="form-control" accept=".csv" id="csv" type="file" (change)="onFileSelect($event.target)" name="myfile">
            </div>
      </div> 
    

  </div>

4 Comments

This will fail for values that have a comma within. You may want to enhance your for loop to account for those cases.
I've used your solution to transform a scraped facebook friendslist using this chrome extension, but i can't access the properties from the json objects inside the returned area. After doing some research i've found out that the JSON objects have stringified properties for some reason. So jsonObj."prop1" instead of jsonObj.prop1. I have no idea what is causing this but adding .replace(/"/g, "") before pushing the property solves the problem. You can test this yourself by downloading..
the chrome extension and scraping a facebook friendlist with it. When you print the properties of one of the resulting json objects inside the array with console.log(Object.getOwnPropertyNames(that.convertedArray[0])); you'll see this ['"prop1"', '"prop2'"] instead of ['prop1', 'prop2']. I've made an edit to your code to include .replace(/"/g, "") to prop.push(line.split(',')[k].replace(/ /g, ''). I hope you don't mind.
I just found out that the problem also occurs with the property values as well. Both the properties as well as the values are quoted with double quote characters twice for some reason. .replace(/"/g, "") can be used on the values as well to solve this problem. You should also do a null check first in case a property value of a specific row happens to be undefined
2

HTML

<input type="file" accept=".csv (change)="csv2Array($event)">

Typescript

csv2Array(fileInput: any){
//read file from input
this.fileReaded = fileInput.target.files[0];

let reader: FileReader = new FileReader();
reader.readAsText(this.fileReaded);

 reader.onload = (e) => {
 let csv: string = reader.result;
 let allTextLines = csv.split(/\r|\n|\r/);
 let headers = allTextLines[0].split(',');
 let lines = [];

  for (let i = 0; i < allTextLines.length; i++) {
    // split content based on comma
    let data = allTextLines[i].split(',');
    if (data.length === headers.length) {
      let tarr = [];
      for (let j = 0; j < headers.length; j++) {
        tarr.push(data[j]);
      }

     // log each row to see output 
     console.log(tarr);
     lines.push(tarr);
  }
 }
 // all rows in the csv file 
 console.log(">>>>>>>>>>>>>>>>>", lines);
} 
}

1 Comment

this is not giving JSON Object instead it is giving just a normal Array
0
import * as XLSX from 'xlsx'


uploadCSVFile(event) {
  const target: DataTransfer = <DataTransfer>(event.target);
  if (target.files.length !== 1) {
    throw new Error('Cannot use multiple files');
    }

    const filereader: FileReader = new FileReader();
    const selectedfile =  event.target.files[0];
    filereader.readAsBinaryString(selectedfile);
    filereader.onload = (event:any)=>{
      let binarydata =  event.target.result;
      let workbook  = XLSX.read (binarydata,{type:'binary'})
      workbook.SheetNames.forEach(sheet=>{
        const data = XLSX.utils.sheet_to_json(workbook.Sheets[sheet]);
        console.log(data);
      })
      
    }


}

do npm install xlsx;

in html

<input type="file" class="upload" (change)="uploadCSVFile($event)">

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.