2013-04-16 35 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 _ => ("", "") 
    | } 
相關問題