Search code examples
jsonscalaopencsv

Creating csv file in Scala


I am trying to to create a csv file in Scala by getting data in the form of JsValue from a third part API. My method to save data to CSV file is :

def saveToCSV(str:Seq[JsValue]) = {
  val outputFile = new BufferedWriter(new FileWriter("Result.csv"))
  val csvWriter = new CSVWriter(outputFile)
    val data = str.head
    data match {
      case JsObject(fields) => {
        var listOfRecords = new ListBuffer[Array[String]]()
        //val csvFields = Array("Open","High","Low","Close","Volume")
        //listOfRecords += csvFields
        fields.values.foreach(value => {
          val jsObject = value.asJsObject()
          val nameList = List(jsObject.fields("1. open").toString,jsObject.fields("2. high").toString,jsObject.fields("3. low").toString,jsObject.fields("4. close").toString,jsObject.fields("5. volume").toString)
          listOfRecords += Array(nameList.toString)
          csvWriter.writeAll(listOfRecords.toList.asInstanceOf)
          println("Written!")
          outputFile.close()
        })
      }
      case JsNull => println("Null")
    }

In the above code on line **csvWriter.writeAll(listOfRecords.toList.asInstanceOf)** I am getting this exception.

Exception in thread "main" java.lang.ClassCastException: class scala.collection.immutable.$colon$colon cannot be cast to class scala.runtime.Nothing$ (scala.collection.immutable.$colon$colon and scala.runtime.Nothing$ are in unnamed module of loader 'app')

On removing asInstanceOf from csvWriter.writeAll(listOfRecords.toList.asInstanceOf) this line, I get a compile time error on writeAll() method saying that it expects parameter of type util.List[Array[String]]

Could anyone please help me to solve this problem?


Solution

  • You have several mistakes in your code.

    1. asJsObject() is a method that does not exist in JsValue. Instead you should use value.as[JsObject].
    2. jsObject.fields is of type Seq[(String, JsValue)], so you can't call jsObject.fields("1. open"). Instead you should call: jsObject("1. open").toString
    3. Calling listOfRecords.toList.asInstanceOf returns null. You should specify what you want to convert it to. But in order to convert it into a java type, you can just call: listOfRecords.toList.asJava. Don't forget: import scala.jdk.CollectionConverters._

    The complete method is:

    import com.opencsv.CSVWriter
    import play.api.libs.json.{JsNull, JsObject, JsValue}
    
    import java.io.{BufferedWriter, FileWriter}
    import scala.collection.mutable.ListBuffer
    import scala.jdk.CollectionConverters._
    
    def saveToCSV(str: Seq[JsValue]) = {
      val outputFile = new BufferedWriter(new FileWriter("Result.csv"))
      val csvWriter = new CSVWriter(outputFile)
      val data = str.head
      data match {
        case JsObject(fields) => {
          var listOfRecords = new ListBuffer[Array[String]]()
          //val csvFields = Array("Open","High","Low","Close","Volume")
          //listOfRecords += csvFields
          fields.values.foreach(value => {
            val jsObject = value.as[JsObject]
            val nameList = List(jsObject("1. open").toString, jsObject("2. high").toString, jsObject("3. low").toString, jsObject("4. close").toString, jsObject("5. volume").toString)
            listOfRecords += Array(nameList.toString)
            csvWriter.writeAll(listOfRecords.toList.asJava)
            println("Written!")
            outputFile.close()
          })
        }
        case JsNull => println("Null")
      }
    }
    

    This will work under the assumption that the head of str contains a JsObject, with the fields:

    • "1. open"
    • "2. high"
    • "3. low"
    • "4. close"

    Are you deliberately taking only the first element of the input to this function? Please note that str.head is unsafe, in case the sequence is empty.