在Scala中,使用Try操作处理项目列表,并保留原始项目以报告可能的失败

时间:2016-10-14 12:55:55

标签: scala scalaz

我有一个字符串列表,我想对每个项目进行几次转换。我想保留原始字符串,以便我可以显示它是否无效。示例如下:

import scala.util.Try

val list = List("<entry id='1'/>", "haha", "<entry id='hehe'/>")

def parseXML(str: String) = Try { xml.XML.loadString(str) }

list
  .map(parseXML)
  .map(tryEntry => tryEntry.map(entry => (entry \ "@id").text))
  .map(tryId => tryId.flatMap(id => Try(id.toInt)))

// here I lose the original string
res17: List[Try[Int]] = List(
  Success(1),
  Failure(org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog.),
  Failure(java.lang.NumberFormatException: For input string: "hehe")
)

// here I keep a copy of the original string, so I can report the invalid entry to the user
list
  .map(l => (l, parseXML(l)))
  .map { case(line, tryEntry) => (line, tryEntry.map(entry => (entry \ "@id").text)) }
  .map { case(line, tryId) => (line, tryId.flatMap(id => Try(id.toInt))) }

res19: List[(String, Try[Int])] = List(
  ("<entry id='1'/>", Success(1)),
  ("haha", Failure(org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog.)),
  ("<entry id='hehe'/>", Failure(java.lang.NumberFormatException: For input string: "hehe"))
)

在res19中,我保留了原始字符串的副本,因此我可以报告错误和原始字符串。但是,我需要在映射操作期间每次都携带这些信息,这很难看。有没有更好的办法? (也许使用ScalaZ Statefor?)

3 个答案:

答案 0 :(得分:1)

这可以通过添加额外的特征ParseContext[T]来优雅地解决,该特征除了原始上下文之外,例如行号和原始&#39; text,是一个Functor(.map(f: T => T))和一个Monad(.flatMap(f: T => ParseContext[T]))。您可以选择创建两个名为ParseContextSuccess的{​​{1}}实例,以指示解析期间可能出现的故障。

基本上,您可以使用一些额外的上下文信息来扩展Failure特征。

答案 1 :(得分:0)

也许,将多个map合并为一个会有所帮助吗?

 val result: List[(String, Try[Int])] = list
   .map { line =>
      line -> parseXML(line)
        .map(_ \ "@id")
        .text
        .flatMap(id => Try(id.toInt)
   }

答案 2 :(得分:0)

如果列表中没有重复的字符串,您可以将其设为地图,然后mapValues覆盖它:

(l zip l toMap)
   .mapValues(parseXML)
   .mapValues(tryEntry => tryEntry.map(entry => (entry \ "@id").text))
   .mapValues(tryId => tryId.flatMap(id => Try(id.toInt)))
   .toList