2013-04-16 44 views
4

比方说我给定的XML如下:优雅的方式来从任一提取元素值[字符串,选项[NodeSeq]]

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

我给定的函数:getXmlStream其具有以下特征:

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

当我调用getXmlStream并传入一个路径时,我会得到左边是错误,右边是选项[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元素提取更具动态性?

+1

如何使用斯卡拉斯验证?这是一个很好的改进,要么 – Edmondo1984

+0

@ Edmondo1984 - 谢谢。我不熟悉Scalaz验证。如果你能提供一个如何在这里使用它的例子,那将是非常好的。 –

回答

1

这里是一个班轮这是我觉​​得还是蛮清楚的:

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

我们可以通过怎么在这里上的步骤:首先我们采取正确的通过将Left映射到None来将其变成Option。现在我们有一个嵌套的Option,我们可以拼合得到Option[NodeSeq]。然后我们将Option的可能形状折叠起来(例如,请参阅this answer以及有关更多讨论的链接)。

请注意,fold仅在2.10中的Option上出现。如果你正在使用一个版本早于斯卡拉的工作,以下是完全等同:

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

标准库并没有在时尚与元组工作的你上描述的方式提供多少段落,但有库,如ScalazShapeless这样做。例如,Scalaz的Bifunctor,你可以写你的当前版本是这样的:

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

而且无形将使你的元素更方便地添加到元组(但这是一个有点复杂,并在新的可能是最好的解决题)。

+0

非常感谢。这是非常翔实的。 –

1

这个怎么样:

scala> getXmlStream(path) match { 
    | case Right(Some(xml)) => (xml\\"c" text, xml\\"d" text) 
    | case _ => ("", "") 
    | } 
相关问题