Scala: Override toString so quotes are printed around strings
Asked Answered
E

2

6

I'd like to write a "toSource" function that will generate the source code for basic case classes. For example, I'd like:

case class Person(name: String, age: Int)
val bob = Person("Bob", 20)
println(toSource(bob)) // Should print """Person("Bob", 20)"""

The "toString" function almost gives me what I want, but it drops the quotes around strings:

println(bob.toString) // Prints """Person(Bob, 20)"""

Any ideas how to do this?

Engadine answered 30/10, 2012 at 23:11 Comment(0)
T
7

You could exploit the fact that case classes mix in trait Product:

def toSource(p: Product): String =
   p.productIterator.map {
      case s: String => "\"" + s + "\""
      case other => other.toString
   } mkString (p.productPrefix + "(", ", ", ")")

toSource(Person("Bob", 20))  // yields """Person("Bob", 20)"""
Thanatos answered 30/10, 2012 at 23:37 Comment(2)
That works! And it can be made to work with nested case classes by adding another case: "case p: Product => toSource(p)"Engadine
I would love it if there was some kind of magic compiler flag or something to make this the default behaviour, then one can distinguish between Set() and Set("")Stimulus
C
0

Building up on the solution provided by 0__, I wrote this approach that works with clases that use only a few primitive types:

object Main {

  def toSourcePrimitive(l: Long): String =
    l.toString + "L"

  def toSourcePrimitive(i: Int): String =
    i.toString

  def toSourcePrimitive(s: String): String =
    "\"" + s.replace("\"", "\\\"") + "\""

  def toSourcePrimitive[T](li: List[T]): String = 
    "List(" + li.map(toSource).mkString(", ") + ")"

  def toSourcePrimitive[T](op: Option[T]): String = 
    if (op.isEmpty) "None" else "Some(" + toSource(op.get) + ")"

  def toSourcePrimitive(p: Product): String = {
    p.productIterator.map(toSource).mkString(p.productPrefix + "(", ", ", ")")
  }

  def toSource(a: Any): String = a match {
    case in: Int       => toSourcePrimitive(in)
    case ln: Long      => toSourcePrimitive(ln)
    case s: String     => toSourcePrimitive(s)
    case li: List[_]   => toSourcePrimitive(li)
    case op: Option[_] => toSourcePrimitive(op)
    case p: Product    => toSourcePrimitive(p)
    case _             => "ERROR"
  }

  case class Example1(date: Int, numLong: Long, numInt: Int)

  case class Example2(v1: String, v2: List[String], v3: List[Example1], v4: Option[String])
  
  def main(args: Array[String]): Unit = {
    println("\nExample 1:")
    println(toSource(Example1(20220105, 6L, 10)))

    println("\nExample 2:")
    val elems = List(
      Example2("blah", List("one", "two"), List(Example1(20220105, 6L, 10)), Some("three")),
      Example2("blah", Nil, List(Example1(20220105, 6L, 10)), None)
    )
    println(toSource(elems))    
  }
}

The output is shown below:

Example 1:
Example1(20220105, 6L, 10)

Example 2:
List(Example2("blah", List("one", "two"), List(Example1(20220105, 6L, 10)), Some("three")), Example2("blah", List(), List(Example1(20220105, 6L, 10)), None))
Cuccuckold answered 10/1, 2023 at 19:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.