如何在二进制序列化数据中添加evro4到avro4s

时间:2018-01-29 17:45:43

标签: scala avro4s

我有一个这样的课程:

case class SurveyTemplate(id:UUID,
                          name: String,
                          version: Int,
                          preDefinedChapter: Seq[Chapter]) {
}

我用DAO将其序列化为DAO中的二进制文件。

def save(input: SurveyTemplate) = Future{
    val target = toFile(input)
    val output = AvroOutputStream.binary[SurveyTemplate](target)
    output.write(input)
    output.flush()
    output.close()
    input
  }

现在我将文件二进制文件保存到硬盘。一切都很好。 现在我想用一个像“dummy:String”

这样的新字段来修改SurveyTemplate

如果我现在加载文件并尝试反序列化,我会得到一个java.util.NoSuchElementException,这意味着它们不是可以反序列化的文件。如果我删除附加字段,一切正常。

以下是列表方法:

def list(name: Option[String] = None,
                    version: Option[Int] = None,
                    authorName: Option[String] = None,
                    drop: Int = 0,
                    take: Int = 100) = Future{
    storageDirectory.listFiles(new FilenameFilter {
      override def accept(dir: File, name: String): Boolean = name.endsWith(ending)
    }).map{e =>
      println("FOUND " + e.getName)    
      val i = AvroInputStream.binary[SurveyTemplate](e)
      println("BINARY " + i)
      println(i.records.size())
      println( Try(i.iterator().toSeq.head) )
      val p = i.iterator().toSeq.head
      i.close()
      println(p)
      println("------------------")
      Some(p)
    }.filter(_.isDefined).slice(drop,take).map(_.get)
  }

我如何添加进化的可能性?

谢谢。

添加了: 打印结果

BINARY com.sksamuel.avro4s.AvroBinaryInputStream@58d20d47
0
Failure(java.util.NoSuchElementException: head of empty stream)

我的新SurveyTemplate:

case class SurveyTemplate(id:UUID,
                          name: String,
                          version: Int,
                          preDefinedChapter: Seq[Chapter], 
                          dummy: Option[String] = None) {
}

现在是最小的

import java.io.{File, FilenameFilter}
import java.util.UUID

import com.sksamuel.avro4s.{AvroInputStream, AvroOutputStream, AvroSchema, SchemaFor}

import scala.util.Try

object Main {

  def main(args: Array[String]): Unit ={
    val dao = new SurveyTemplateDAO
    //RUN THIS FIRST!
    val first = SurveyTemplate(UUID.randomUUID(),"TEST",0,"ICH",Seq(Chapter(1,1,"HELLO",Nil)))
    dao.save(first)
    //COMMENT OUT! Then uncomment the dummy in SurveyTemplate

    //RUN THIS AFTER FILE SAVED
    dao.list()

  }

}


case class Chapter(counter: Int,
                   level: Int,
                   title: String,
                   subChapters: Seq[Chapter]) {

}

case class SurveyTemplate(id:UUID,
                          name: String,
                          version: Int,
                          authorName: String,
                          preDefinedChapter: Seq[Chapter]/*, dummy: Option[String] = None*/) {

}

class SurveyTemplateDAO {

  private val ending = ".pst"
  private val storageDirectory = new File("C:/Users/André Schmidt/Desktop/tmp")

  def toFileName(x: SurveyTemplate): String = toFileName(x.id)
  def toFileName(x: UUID): String = s"$x$ending"
  def toFile(x: SurveyTemplate) = new File(storageDirectory.getCanonicalPath+File.separator+toFileName(x))

  private implicit val schemaForChapter = SchemaFor[Chapter]

  def save(input: SurveyTemplate) = {
    val target = toFile(input)
    val output = AvroOutputStream.binary[SurveyTemplate](target)
    output.write(input)
    output.flush()
    output.close()
    input
  }


  def list(name: Option[String] = None,
           version: Option[Int] = None,
           authorName: Option[String] = None,
           drop: Int = 0,
           take: Int = 100) = {

    storageDirectory.listFiles(new FilenameFilter {
      override def accept(dir: File, name: String): Boolean = name.endsWith(ending)
    }).take(1).foreach{e =>
      println("OTHER WAY")
      val i = AvroInputStream.binary[SurveyTemplate](e)
      println("BINARY " + i)
      println(i.records.size())
      //println( Try(i.iterator().toList.head) )
      val p = Try(i.iterator().toList.head).toOption
      i.close()
      println(p)
      println("OTHER WAY ------------------")
      println("OTHER WAY ------------------")
      println("OTHER WAY ------------------")
    }


    storageDirectory.listFiles(new FilenameFilter {
      override def accept(dir: File, name: String): Boolean = name.endsWith(ending)
    }).map{e =>
      println("FOUND " + e.getName)
      //      val schema = SchemaBuilder.builder()
      //      val r = Schema.createRecord(java.util.Arrays.asList())
      //      val t = new AvroBinaryInputStream[SurveyTemplate](new SeekableFileInput(e),readerSchema = Some(r))
      //      println(t)

      val i = AvroInputStream.binary[SurveyTemplate](e,AvroSchema[SurveyTemplate]/*,AvroSchema[OldSurveyTemplate]*/)
      println("BINARY " + i)
      println(i.records.size())
      //println( Try(i.iterator().toList.head) )
      val p = Try(i.iterator().toList.head).toOption
      i.close()
      println(p)
      println("------------------")
      println("ENDE")
      p
      //      if( authorName.map(_.equalsIgnoreCase(p.authorName)).getOrElse( name.map(_.equalsIgnoreCase(p.name) ).getOrElse( version.forall(_ == p.version) ) ) ){
      //        Some(p)
      //      } else None
    }.filter(_.isDefined).slice(drop,take).map(_.get)
  }

}

我的sbt:

name := "avroTest"

version := "0.1"

scalaVersion := "2.12.4"

libraryDependencies ++= Seq(
  "com.sksamuel.avro4s" %% "avro4s-core" % "1.8.1"
)

2 个答案:

答案 0 :(得分:0)

为了使SurveyTemplate向后兼容,我们必须确保新读者可以从旧作者那里读取数据。这意味着新参数必须具有默认值,因为旧编写器无法提供它。这意味着:

case class SurveyTemplate(id:UUID,
                          name: String,
                          version: Int,
                          preDefinedChapter: Seq[Chapter],
                          dummyString: String = “”)

dummyString Option[String]

另一件事是,除非您将编写器架构提供给序列化器,否则不得更改参数的顺序。

答案 1 :(得分:0)

更改AvroInputStream.binary tp AvroInputStream.data允许我添加进化! 这不是很好,因为我的文件的二进制表示对我来说更好。

但在二进制中所有测试都失败了!