Blankman
Blankman

Reputation: 267230

Serialize / Deserialize Object To/From Delimited String

Say I have a case class like:

case class User(id: Int, name: String, age: Int)

And this is also stored in a file, where each line looks like:

123|"john"|35

I want to perform this parsing back and forth between a User object and the string representation in a single place.

What is the best way to do this in Scala?

I know I can just do a string split, but wondering if their is a cleaner approach and more idiomatic.

Upvotes: 1

Views: 553

Answers (1)

John Mullins
John Mullins

Reputation: 1101

  /**
   * Serialization/deserialization API
   */    
  trait Codec[T] {
    def parse(str: String):Option[T]

    def write(u: T): String

    implicit def string2type(str: String):Option[T] = parse(str)

    implicit def type2string(u: T):String = write(u)

    implicit def iterable2type(it: Iterable[String]):Iterable[T] = 
        it.flatMap(string2type)

  }

  /**
    * Serialization/deserialization implementation for User
    */
  object UserCodec extends Codec[User] {
    val pattern = """(\d+)\|"([\w\s]+)"\|(\d+)""".r

    override def parse(str: String):Option[User] = str match {
      case pattern(id, name, age) => Some(User(id.toInt, name, age.toInt))
      case _ => None
    }

    override def write(u: User): String = s"""${u.id}|"${u.name}"|${u.age}"""

  }

Usage:

def main(args: Array[String]): Unit = {
    import UserCodec._

    val str = """123|"john asd"|35"""

    val u:Option[User] = str
    val s:String = User(1, "a", 1)

    println(u)   // out: Some(User(123,john asd,35))
    println(s)   // out: 1|"a"|1
}

Upd: Example with iterables:

def main(args: Array[String]): Unit = {
    import UserCodec._

    val lines = Seq(
      """1|"john"|34""",
      """2|"bill"|35""",
      """3|"aaa"|36"""
    )

    val validUsers: Iterable[User] = lines

    println(validUsers.toList)  
    //out:List(User(1,john,34), User(2,bill,35), User(3,aaa,36))
}

Upvotes: 3

Related Questions