5

我正在尝试用 Haskell 解析一些 XML 文件。对于这项工作,我使用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

提前致谢!

4

2 回答 2

5

我认为选择“/root/a[2]/b”的解决方案(第二个“a”标签内的所有“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 aArrowXml a子类也是ArrowList。通过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"必须用括号括起来。

于 2013-07-23T10:55:04.127 回答
1

这只是EarlGray答案的扩展。参见和的>>.解释>.!问完这个问题后,我意识到我需要以一种特殊且确定的方式穿过树。所以这是我用于解决特定问题的解决方案。对于其他人试图完成同样事情的情况,我想分享示例代码。

假设我们要提取 first<a>和 second的文本<b>。并非所有<a>元素都至少有两个<b>s,因此EarlGray的代码会退出,因为您不能使用该(!!)函数(空列表!)。

查看Control.Arrow.ArrowList中的函数single,它仅使用列表箭头的第一个结果:

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

提取值并返回一个 Maybe 值:

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

Just "second element"与示例 XML 文件一起输出。

于 2013-07-24T09:41:33.750 回答