使用com-lihaoyi/PPrint打印case class属性名遇挫求助
解决com-lihaoyi/PPrint打印case class带属性名的问题
错误原因
你的代码抛出MatchError是因为productIterator.toSeq.map里用了case (name, value)模式匹配,但productIterator返回的是case class属性值的迭代器,不是键值对元组,匹配失败导致报错。
可行解决方案
要打印case class的属性名,需要把case class的字段名和对应的值配对。Scala 2.13及以上版本可直接用productElementNames获取字段名,结合productIterator的值构造带属性名的输出;同时要递归调用自定义处理器,保证嵌套结构也能正确处理。
修正后的代码:
import fansi.Str import pprint._ object Testo extends App { case class Person(name: String, age: Int, address: Address) case class Address(street: String, city: String) val person = Person("John Doe", 30, Address("123 Main St", "New York")) val list = List(person, person) val map2 = Map("foo" -> list, "bar" -> person) val pprint2 = pprint.copy( additionalHandlers = { case p: Product if p.productArity > 0 => // 配对字段名与对应属性值 val fieldPairs = p.productElementNames.zip(p.productIterator) // 递归处理每个属性值,保留pprint的格式化特性 val fieldTrees = fieldPairs.map { case (name, value) => Tree.Apply( Tree.Literal(fansi.Str(s"$name = ")), List(walker.treeify(value, pprint2)) ) } // 构造case class的树形输出结构 Tree.Apply( Tree.Literal(fansi.Str(p.productPrefix)), List(Tree.Tuple(fieldTrees.toList)) ) } ) // 打印最终结果 pprint2.pprintln(map2) }
关键细节说明
productElementNames:Scala 2.13引入的方法,直接返回case class的字段名迭代器,无需反射,简洁高效。若使用Scala 2.12及以下版本,需通过反射API获取字段名(比如p.getClass.getDeclaredFields.map(_.getName)),注意过滤编译器生成的冗余字段(如MODULE$)。- 递归处理嵌套结构:用
walker.treeify(value, pprint2)递归处理每个属性值,确保嵌套的case class(如示例中的Address)也能打印出属性名。 - 保留格式化特性:避免直接拼接字符串返回
Literal,而是构造Tree.Apply和Tree.Tuple结构,让pprint保持原有的换行、缩进等格式化能力。 showFieldNames参数:该参数针对pprint内置类型(如Map键值对)生效,无法控制case class的属性名显示,因此之前设置该参数无效。
内容的提问来源于stack exchange,提问作者Chris Bedford
相关产品推荐
相关产品推荐

