将JSON写入文件时出现序列化错误

时间:2015-05-28 13:55:13

标签: json scala playframework apache-spark

我正在读取文本文件并在每次迭代中创建Json对象JsValues。我想在每次迭代时将它们保存到文件中。我正在使用Play Framework来创建JSON对象。

class Cleaner {
  def getDocumentData() = {
     for (i <- no_of_files) {
     .... do something ...
         some_json = Json.obj("text" -> LARGE_TEXT)
         final_json = Json.stringify(some_json)
         //save final_json here to a file
     }
  }
}

我尝试使用PrintWriter保存json,但我收到Exception in thread "main" org.apache.spark.SparkException: Task not serializable错误。

我该如何纠正?或者还有其他方法可以保存JsValue吗?

更新:

我读到在这种情况下必须使用特征serializable。我有以下功能:

class Cleaner() extends Serializable {
  def readDocumentData() {
    val conf = new SparkConf()
      .setAppName("linkin_spark")
      .setMaster("local[2]")
      .set("spark.executor.memory", "1g")
      .set("spark.rdd.compress", "true")
      .set("spark.storage.memoryFraction", "1")

    val sc = new SparkContext(conf)

    val temp = sc.wholeTextFiles("text_doc.dat)
    val docStartRegex = """<DOC>""".r
    val docEndRegex = """</DOC>""".r
    val docTextStartRegex = """<TEXT>""".r
    val docTextEndRegex = """</TEXT>""".r
    val docnoRegex = """<DOCNO>(.*?)</DOCNO>""".r
    val writer = new PrintWriter(new File("test.json"))

    for (fileData <- temp) {
      val filename = fileData._1
      val content: String = fileData._2
      println(s"For $filename, the data is:")
      var startDoc = false // This is for the
      var endDoc = false // whole file
      var startText = false //
      var endText = false //
      var textChunk = new ListBuffer[String]()
      var docID: String = ""
      var es_json: JsValue = Json.obj()

      for (current_line <- content.lines) {
        current_line match {
          case docStartRegex(_*) => {
            startDoc = true
            endText = false
            endDoc = false
          }
          case docnoRegex(group) => {
            docID = group.trim
          }
          case docTextStartRegex(_*) => {
            startText = true
          }
          case docTextEndRegex(_*) => {
            endText = true
            startText = false
          }
          case docEndRegex(_*) => {
            endDoc = true
            startDoc = false
            es_json = Json.obj(
              "_id" -> docID,
              "_source" -> Json.obj(
                "text" -> textChunk.mkString(" ")
              )
            )
            writer.write(es_json.toString())
            println(es_json.toString())
            textChunk.clear()
          }
          case _ => {
            if (startDoc && !endDoc && startText) {
              textChunk += current_line.trim
            }
          }
        }
      }
    }
    writer.close()
  }
}

这是我添加了特征的功能,但我仍然得到相同的异常。 我重写了一个较小的版本:

def foo() {
    val conf = new SparkConf()
      .setAppName("linkin_spark")
      .setMaster("local[2]")
      .set("spark.executor.memory", "1g")
      .set("spark.rdd.compress", "true")
      .set("spark.storage.memoryFraction", "1")
    val sc = new SparkContext(conf)

    var es_json: JsValue = Json.obj()
    val writer = new PrintWriter(new File("test.json"))
    for (i <- 1 to 10) {
      es_json = Json.obj(
        "_id" -> i,
        "_source" -> Json.obj(
          "text" -> "Eureka!"
        )
      )
      println(es_json)
      writer.write(es_json.toString() + "\n")
    }
    writer.close()
  }

此功能可以正常使用,也可以不使用serializable。我无法理解发生了什么?

1 个答案:

答案 0 :(得分:1)

编辑:第一个电话答案。

不是您的主类需要可序列化,而是您在for (fileData <- temp)内的rdd处理循环中使用的类 它需要是可序列化的,因为spark数据位于可能位于多台计算机上的多个分区上。因此,应用于此数据的函数需要是可序列化的,因此您可以将它们发送到将要并行执行的其他计算机。 PrintWriter无法序列化,因为它引用的文件只能从原始计算机上获得。因此序列化错误。

在初始化spark过程的计算机上写入数据。您需要获取整个群集中的数据并将其带到您的计算机上然后编写它。

为此,您可以收集结果。 rdd.collect()这将获取集群中的所有数据并将其放入驱动程序线程内存中。然后,您可以使用PrintWriter将文件写入文件。

像这样:

temp.flatMap { fileData =>
  val filename = fileData._1
  val content: String = fileData._2
  println(s"For $filename, the data is:")
  var startDoc = false // This is for the
  var endDoc = false // whole file
  var startText = false //
  var endText = false //
  var textChunk = new ListBuffer[String]()
  var docID: String = ""
  var es_json: JsValue = Json.obj()

  var results = ArrayBuffer[String]()

  for (current_line <- content.lines) {
    current_line match {
      case docStartRegex(_*) => {
        startDoc = true
        endText = false
        endDoc = false
      }
      case docnoRegex(group) => {
        docID = group.trim
      }
      case docTextStartRegex(_*) => {
        startText = true
      }
      case docTextEndRegex(_*) => {
        endText = true
        startText = false
      }
      case docEndRegex(_*) => {
        endDoc = true
        startDoc = false
        es_json = Json.obj(
          "_id" -> docID,
          "_source" -> Json.obj(
            "text" -> textChunk.mkString(" ")
          )
        )
        results.append(es_json.toString())
        println(es_json.toString())
        textChunk.clear()
      }
      case _ => {
        if (startDoc && !endDoc && startText) {
          textChunk += current_line.trim
        }
      }
    }
  }

  results
}
.collect()
.foreach(es_json => writer.write(es_json))

如果结果对于驱动程序线程内存来说太大,则可以使用saveAsTextFile函数将每个分区流式传输到驱动器。在第二种情况下,您作为参数提供的路径将被创建到一个文件夹中,并且您的rdd的每个分区将被写入其中的编号文件。

像这样:

temp.flatMap { fileData =>
  val filename = fileData._1
  val content: String = fileData._2
  println(s"For $filename, the data is:")
  var startDoc = false // This is for the
  var endDoc = false // whole file
  var startText = false //
  var endText = false //
  var textChunk = new ListBuffer[String]()
  var docID: String = ""
  var es_json: JsValue = Json.obj()

  var results = ArrayBuffer[String]()

  for (current_line <- content.lines) {
    current_line match {
      case docStartRegex(_*) => {
        startDoc = true
        endText = false
        endDoc = false
      }
      case docnoRegex(group) => {
        docID = group.trim
      }
      case docTextStartRegex(_*) => {
        startText = true
      }
      case docTextEndRegex(_*) => {
        endText = true
        startText = false
      }
      case docEndRegex(_*) => {
        endDoc = true
        startDoc = false
        es_json = Json.obj(
          "_id" -> docID,
          "_source" -> Json.obj(
            "text" -> textChunk.mkString(" ")
          )
        )
        results.append(es_json.toString())
        println(es_json.toString())
        textChunk.clear()
      }
      case _ => {
        if (startDoc && !endDoc && startText) {
          textChunk += current_line.trim
        }
      }
    }
  }

  results
}
.saveAsTextFile("test.json")