假设我得到了如下XML:
<a><b><c>hello</c><d>world</d><e>again</e></b></a>
我得到一个函数:getXmlStream,它具有以下签名:
def getXmlStream(xmlPath:String):Either[String,Option[NodeSeq]]
当我调用带有路径的getXmlStream时,我会得到Either,其中Left是错误,右边是Option [NodeSeq]。
现在,如果NodeSeq不是None,我需要获取元素的值,分别是“hello”和“world”。
我尝试获取元素如下:
val elems = (getXmlStream(xmlFilePath)) match {
case Left(error:String) => None
case Right(xmlStreamOpt) => {
xmlStreamOpt map {
(r \\ "c" text, r \\ "d" text)
}
}
}).getOrElse("","")
elems
现在将包含一个值为("hello","world")
的元组,如果它们存在,那么它将是一个空字符串的元组。
我不认为我上面写的片段是惯用的scala。有人可以建议我如何重构它。
我觉得第二个问题是我在解码片段中硬编码节点“c”和“d”。如果现在,要求提取“e”,我是否会将表达式(r \\ "c" text, r \\ "d" text)
修改为(r \\ "c" text, r \\ "d" text, r \\ "e" text)
?是否有可能使xml元素提取更具动态性?
答案 0 :(得分:1)
这个怎么样:
scala> getXmlStream(path) match {
| case Right(Some(xml)) => (xml\\"c" text, xml\\"d" text)
| case _ => ("", "")
| }
答案 1 :(得分:1)
这是一个单行,我认为仍然很清楚:
res.right.toOption.flatten.fold(("", ""))(r => (r \\ "c" text, r \\ "d" text))
我们可以逐步完成这里发生的事情:首先我们对Either
进行正确的投影,并通过将Option
映射到Left
将其转换为None
。现在我们有一个嵌套的Option
,我们可以将其展平以获得Option[NodeSeq]
。然后我们折叠Option
的可能形状(参见例如this answer及其中的链接以进行更多讨论)。
请注意,fold
仅出现在2.10中的Option
上。如果您之前使用的是Scala版本,则以下内容完全等效:
res.right.toOption.flatten.map(
r => (r \\ "c" text, r \\ "d" text)
).getOrElse(("", ""))
标准库并没有提供您在上一段中描述的方式使用元组的方式,但有Scalaz和Shapeless等库可以。例如,使用Scalaz的Bifunctor
,您可以编写当前版本:
res.right.toOption.flatten.fold(("", ""))(r => ("c", "d").umap(r \\ _ text))
而Shapeless将允许您更容易地向元组添加元素(但这有点复杂,可能最好在新问题中解决)。