Dynamically create case class

前端 未结 3 1756
北恋
北恋 2020-12-10 22:24

I am using a csv library that takes a case class and turns it into rows for me to read.

The syntax is pretty close to File(path).asCsvReader[caseClass].

3条回答
  •  忘掉有多难
    2020-12-10 22:56

    With hints from dveim in this answer, I'm adding a second solution that works both in Scala 2.10 and 2.11 and uses Scala Toolbox. The generated cases classes unfortunately are in the default package.

    Generate Case Class

    /**
      * Generate a case class and persist to file
      * Can't add package to the external class
      * @param file External file to write to
      * @param className Class name
      * @param header case class parameters
      */
    def writeCaseClass(file: File, className: String, header: String): Unit = {
      val writer: PrintWriter = new PrintWriter(file)
      writer.println("case class " + className + "(")
      writer.println(header)
      writer.println(") {}")
      writer.println("\nscala.reflect.classTag[" + className + "].runtimeClass")
      writer.flush()
      writer.close()
    }
    

    Compile external classes using Toolbox

    /**
      * Helper method to search code files in directory
      * @param dir Directory to search in
      * @return
      */
    def recursiveListFiles(dir: File): Array[File] = {
      val these = dir.listFiles
      these ++ these.filter(_.isDirectory).flatMap(recursiveListFiles)
    }  
    
    /**
      * Compile scala files and keep them loaded in memory
      * @param classDir Directory storing the generated scala files
      * @throws IOException if there is problem reading the source files
      * @return Map containing Class name -> Compiled Class Reference
      */
    @throws[IOException]
    def compileFiles(classDir: String): Map[String, Class[_]] = {
      val tb = universe.runtimeMirror(getClass.getClassLoader).mkToolBox()
    
      val files = recursiveListFiles(new File(classDir))
        .filter(_.getName.endsWith("scala"))
      println("Loaded files: \n" + files.mkString("[", ",\n", "]"))
    
      files.map(f => {
        val src = Source.fromFile(f).mkString
        val clazz = tb.compile(tb.parse(src))().asInstanceOf[Class[_]]
        getClassName(f.getName) -> clazz
      }).toMap
    }
    

    Instantiate and Use compiled classes

    The compiled class can be obtained from the map and used where necessary e.g.

    //Test Address class
    def testAddress(map: Map[String, Class[_]]) = {
      val addressClass = map("AddressData")
      val ctor = addressClass.getDeclaredConstructors()(0)
      val instance = ctor.newInstance("123 abc str", "apt 1", "Hello world", "HW", "12345")
      //println("Instantiated class: " + instance.getClass.getName)
      println(instance.toString)
    }
    

提交回复
热议问题