2013-07-22 25 views
3

我想用Haskell解析一些XML文件。對於這項工作,我正在使用HXT獲得有關現實世界中的箭頭的知識。所以我對箭頭主題很陌生。HXT:在Haskell中用HXT按位置選擇一個節點?

在XPath(和HaXml),所以可以選擇通過位置的節點,讓我們說:/root/a[2]/b

我無法弄清楚如何做這樣的事情與HXT,甚至一次又一次地閱讀文檔後。

下面是一些示例代碼,我的工作:

module Main where 

import Text.XML.HXT.Core 

testXml :: String 
testXml = unlines 
    [ "<?xml version=\"1.0\"?>" 
    , "<root>" 
    , " <a>" 
    , "  <b>first element</b>" 
    , "  <b>second element</b>" 
    , " </a>" 
    , " <a>" 
    , "  <b>third element</b>" 
    , " </a>" 
    , " <a>" 
    , "  <b>fourth element</b>" 
    , "  <b>enough...</b>" 
    , " </a>" 
    , "</root>" 
    ] 

selector :: ArrowXml a => a XmlTree String 
selector = getChildren /> isElem >>> hasName "a" -- how to select second <a>? 
         /> isElem >>> hasName "b" 
         /> getText 

main :: IO() 
main = do 
    let doc = readString [] testXml 
    nodes <- runX $ doc >>> selector 
    mapM_ putStrLn nodes 

所需的輸出將是:

third element 

提前感謝!

回答

3

我相信選擇溶液 「/根/ A [2]/B」(內側第二 「一個」 標籤的所有 「b」 的標記):

selector :: ArrowXml a => Int -> a XmlTree String 
selector nth = 
    (getChildren /> isElem >>> hasName "a") -- the parentheses required! 
    >. (!! nth) 
    /> isElem >>> hasName "b" /> getText 

(結果是["third element"])。

說明:正如我所看到的,class (..., ArrowList a, ...) => ArrowXml a,所以ArrowXml aArrowList的子類。翻翻ArrowList接口:

(>>.) :: a b c -> ([c] -> [d]) -> a b d 
(>.) :: a b c -> ([c] -> d) -> a b d 

所以>>.可以使用一些解除[c] -> [d]>.可以使用[c] -> d類型的解禁功能從列表中選擇一個項目選擇列表的一個子集。因此,在選擇兒童並標記「a」後,讓我們使用(!! nth) :: [a] -> a

有需要注意的一件重要的事情:

infix 1 >>> 
infix 5 /> 
infix 8 >. 

(所以我有一個艱難的時間,試圖弄清楚爲什麼>.沒有括號不能按預期工作)。因此,getChildren /> isElem >>> hasName "a"必須用圓括號包裝。

+0

非常感謝。這是'(>>。)'和'(>。)'的很好的解釋。這是我錯過的提示。 '(!! nth)'只有一個問題。如果XML文件的元素太少,則會出現錯誤。使用助手函數'helper a = a >>。 (拿1滴(第 - 1))'會阻止這一點。 –

+1

我不想搞砸你的答案,但我想分享我的代碼來選擇樹中的單個元素。以防萬一有人遇到同樣的問題或想要一些代碼來玩。您收到學分;-) –

0

這只是對EarlGray的回答的擴展。請參閱>>.>.的解釋!在提出這個問題之後,我意識到我需要以一種特殊的和確定的方式在樹上走過。所以這是我用於解決特定問題的解決方案。對於其他人試圖完成同樣事情的情況,我想分享示例代碼。

比方說,我們想提取第一個<a>和第二個<b>的文本。並非所有的<a>元素都至少有兩個<b> s,所以EarlGray的代碼將被釋放,因爲您不能使用(!!)函數(空列表!)。

看一看功能singleControl.Arrow.ArrowList,其中僅使用列表箭頭的第一個結果:

single :: ArrowList a => a b c -> a b c 
single f = f >>. take 1 

我們想抽取第n個元素:

junction :: ArrowList a => a b c -> Int -> a b c 
junction a nth = a >>. (take 1 . drop (nth - 1)) 

現在我們可以使用這個新箭頭來構建選擇器。圍繞我們要用junction進行過濾的內容使用括號是必要的,因爲junction會修改現有的箭頭。

selector :: ArrowXml a => a XmlTree String 
selector = getChildren -- There is only one root element. 
     -- For each selected element: Get a list of all children and filter them out. 
     -- The junction function now selects at most one element. 
     >>> (getChildren >>> isElem >>> hasName "a") `junction` 1 -- selects first <a> 
     -- The same thing to select the second <b> for all the <a>s 
     -- (But we had selected only one <a> in this case! 
     -- Imagine commenting out the `junction` 1 above.) 
     >>> (getChildren >>> isElem >>> hasName "b") `junction` 2 -- selects second <b> 
     -- Now get the text of the element. 
     >>> getChildren >>> getText 

要提取值,並返回一個值可能:

main :: IO() 
main = do 
    let doc = readString [] testXml 
    text <- listToMaybe <$> (runX $ doc >>> selector) 
    print text 

此輸出Just "second element"與示例XML文件。