我正在尝试使用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
提前致谢!
答案 0 :(得分:4)
我相信选择“/ 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 a
,ArrowXml 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"
必须用括号括起来。
答案 1 :(得分:0)
这只是EarlGray对答案的延伸。请参阅>>.
和>.
的说明!在提出问题之后,我意识到我需要以特殊和确定的方式穿过树。所以这是我用于解决具体问题的解决方案。对于其他人试图完成同样的事情,我想分享示例代码。
假设我们要提取第一个<a>
和第二个<b>
的文字。并非所有<a>
元素都至少有两个<b>
,因此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文件。