Scala - how to print case classes like (pretty printed) tree

前端 未结 10 1872
情歌与酒
情歌与酒 2020-12-07 22:35

I\'m making a parser with Scala Combinators. It is awesome. What I end up with is a long list of entagled case classes, like: ClassDecl(Complex,List(VarDecl(Real,float

10条回答
  •  无人及你
    2020-12-07 22:58

    This is a shamless copy paste of @F. P Freely, but

    • I've added an indentation feature
    • slight modifications so that the output will be of correct Scala style (and will compile for all primative types)
    • Fixed string literal bug
    • Added support for java.sql.Timestamp (as I use this with Spark a lot)

    Tada!

    // Recursively get all the fields; this will grab vals declared in parents of case classes.
      def getFields(cls: Class[_]): List[Field] =
        Option(cls.getSuperclass).map(getFields).getOrElse(Nil) ++
          cls.getDeclaredFields.toList.filterNot(f =>
            f.isSynthetic || java.lang.reflect.Modifier.isStatic(f.getModifiers))
    
      // FIXME fix bug where indent seems to increase too much
      def prettyfy(a: Any, indentSize: Int = 0): String = {
        val indent = List.fill(indentSize)(" ").mkString
    
        val newIndentSize = indentSize + 2
        (a match {
          // Make Strings look similar to their literal form.
          case string: String =>
            val conversionMap = Map('\n' -> "\\n", '\r' -> "\\r", '\t' -> "\\t", '\"' -> "\\\"", '\\' -> "\\\\")
            string.map(c => conversionMap.getOrElse(c, c)).mkString("\"", "", "\"")
          case xs: Seq[_] =>
            xs.map(prettyfy(_, newIndentSize)).toString
          case xs: Array[_] =>
            s"Array(${xs.map(prettyfy(_, newIndentSize)).mkString(", ")})"
          case map: Map[_, _] =>
            s"Map(\n" + map.map {
              case (key, value) => "  " + prettyfy(key, newIndentSize) + " -> " + prettyfy(value, newIndentSize)
            }.mkString(",\n") + "\n)"
          case None => "None"
          case Some(x) => "Some(" + prettyfy(x, newIndentSize) + ")"
          case timestamp: Timestamp => "new Timestamp(" + timestamp.getTime + "L)"
          case p: Product =>
            s"${p.productPrefix}(\n${
              getFields(p.getClass)
                .map { f =>
                  f.setAccessible(true)
                  s"  ${f.getName} = ${prettyfy(f.get(p), newIndentSize)}"
                }
                .mkString(",\n")
            }\n)"
          // General objects and primitives end up here.
          case q =>
            Option(q).map(_.toString).getOrElse("null")
        })
          .split("\n", -1).mkString("\n" + indent)
      }
    

    E.g.

    case class Foo(bar: String, bob: Int)
    
    case class Alice(foo: Foo, opt: Option[String], opt2: Option[String])
    
    scala> prettyPrint(Alice(Foo("hello world", 10), Some("asdf"), None))
    res6: String =
    Alice(
      foo = Foo(
        bar = "hello world",
        bob = 10
      ),
      opt = Some("asdf"),
      opt2 = None
    )
    

提交回复
热议问题