4

假设我得到了一个 XML,如下所示:

<a><b><c>hello</c><d>world</d><e>again</e></b></a>

我得到了一个函数:getXmlStream,它具有以下签名:

def getXmlStream(xmlPath:String):Either[String,Option[NodeSeq]]

当我用传入的路径调用 getXmlStream 时,我会得到左边是错误,右边是 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 元素提取更加动态?

4

2 回答 2

1

这个怎么样:

scala> getXmlStream(path) match {
     |   case Right(Some(xml)) => (xml\\"c" text, xml\\"d" text)
     |   case _ => ("", "")
     | }
于 2013-04-16T07:01:10.283 回答
1

这是一个我认为仍然很清楚的单线:

res.right.toOption.flatten.fold(("", ""))(r => (r \\ "c" text, r \\ "d" text))

我们可以逐步了解这里发生的事情:首先我们对 进行正确的投影,并通过映射到Either将其转换为。现在我们有一个嵌套的 ,我们可以将其展平以获得一个. 然后我们折叠可能的形状(参见例如这个答案和那里的链接以获得更多讨论)。OptionLeftNoneOptionOption[NodeSeq]Option

请注意,foldOption在 2.10 中出现。如果您使用的是早于该版本的 Scala,则以下内容完全相同:

res.right.toOption.flatten.map(
  r => (r \\ "c" text, r \\ "d" text)
).getOrElse(("", ""))

标准库并没有提供太多以您在上一段中描述的方式处理元组的方式,但是有一些库,例如ScalazShapeless。例如,使用 Scalaz's Bifunctor,您可以像这样编写当前版本:

res.right.toOption.flatten.fold(("", ""))(r => ("c", "d").umap(r \\ _ text))

Shapeless 将允许您更轻松地向元组添加元素(但这有点复杂,可能最好在新问题中解决)。

于 2013-04-16T12:38:20.267 回答