从非标准化的xml获取Cursor的内容

时间:2013-12-15 20:00:43

标签: xml haskell conduit

假设有xml文件:

            <span id="assignee-val">

        <span class="user-hover" id="issue_summary_assignee_m" rel="m">
        <span class="aui-avatar aui-avatar-small"><div class="aui-avatar-inner"><img src="/secure/useravatar?size=small&amp;avatarId=10222" /></div></span>
        This Value!
    </span>
</span>

问题是如何从这个xml中获取"This Value!"

这就是我所拥有的:(

> :m + Control.Applicative Data.ByteString.Lazy Text.HTML.DOM Text.XML.Cursor
> Prelude.map content . (element "span" >=> "id" `attributeIs` "assignee-val" >=> child >=> element "span" >=> "class" `attributeIs` "user-hover" >=> child) . fromDocument . parseLBS <$> Data.ByteString.Lazy.readFile "xmlfile" 
[["\n            "],[],["\n            This Value!\n        "]]
  1. 为什么有3个答案?什么查询会更准确地定义<span class="user-hover">标记内的内容?
  2. 如何自动删除空格缩进和换行符号?
  3. UPD :换句话说,问题是如何删除所有嵌套代码(无论有多少内容)并获得第一级内容 "This Value!"(以及空格和换行符)。

2 个答案:

答案 0 :(得分:2)

  

问题1-为什么有3个答案?

您导航到的数据包含“user-hover”span标记的子项....拉出不重要的东西,您的节点看起来像这样

<span class="user-hover">
    <span />
    This Value!
</span>

XML解析器将其视为

<span class="user-hover">[TextNode "\n    "]<span />[TextNode "\n    This Value!\n"]</span>

因此,“user-hover”元素确实有3个孩子。

[TextNode "\n    ", <span />, TextNode "\n    This Value!\n"]

然后将“内容”应用于这些值中的每一个。由于span元素中没有任何内部内容,因此返回“”,然后得到:

[["\n    "], [], ["\n    This Value!\n"]]

  

问题2-如何自动删除空格缩进和换行符号?

根据xml规范,xml解析器必须保留空间。 XML游标库中可能有工具为您剥离此空间(某些xml处理库为您提供了打开自动后处理空白剥离的选项),但我不知道它。只需在查询后删除另一个调用中的空格。

您可以使用Data.Text.strip函数为您执行空格剥离。


要获得所需的值,您需要在查询中获得更多信息....数据是否始终位于“user-hover”span元素的第三个位置?它总是在<span class="aui-avatar aui-avatar-small" />元素之后吗?是否将用户悬停元素中的所有内容与空格剥离连接起来?一旦你回答这个问题,解决方案应该是显而易见的。


更新回答 -

根据您提供的额外信息,我可以在答案中添加更多信息。

简短的回答是 - 删除“Prelude.map内容”,并在管道中添加“&gt; =&gt;内容”,然后再添加一个Data.Text.concat到最终输出。

以下是详细信息......

Text.XML.Cursor中的几乎所有函数都是a->[a]形式,其中的想法是将每个过滤器应用于节点列表,然后连接结果。这非常类似于XPath中发生的事情,并且明显地模仿了它。

好消息是,我刚才描述的模式正是数组monad的工作原理....如果你使用bind a->[a]将一堆(>>=)函数链接在一起,那么管道基本上就会做管道中每个阶段的concat . map f。当您将map content添加到前面时,它可以正常工作,但只能完成图书馆打算在完整的XPath工具中执行的预期作业的一半。它删除了文本内容,但从未连接结果。以这种方式使用时,content仅返回元素内文本节点中文本的列表。您仍然需要最后一个concat将这些文本项连接在一起。

当我使用管道时:

Data.Text.concat . (child >=> element "span" >=> "id" `attributeIs` "assignee-val" >=> child >=> element "span" >=> "class" `attributeIs` "user-hover" >=> child >=> content) . fromDocument . parseLBS <$> Data.ByteString.Lazy.readFile "file.xml" 

我得到了结果

"\n        \n        This Value!\n    "

如果你想......你仍然可以用Data.Text.strip剥离最终结果。

答案 1 :(得分:1)

有多个答案的原因是user-hover范围有多个孩子:aui-avatar范围之前的孩子(只包含空格),aui-avatar范围和一个包含"This Value!"。要获得最后一个值,您应该只查看结果集的最后一个元素,而不是重写查询:

λ> import Control.Applicative
λ> import qualified Data.ByteString.Lazy as L
λ> import qualified Data.Text as T
λ> import Text.HTML.DOM
λ> import Text.XML.Cursor
λ> :set -XOverloadedStrings
λ> let assignee = element "span" >=> "id" `attributeIs` "assignee-val"
λ> let hover = element "span" >=> "class" `attributeIs` "user-hover"
λ> map T.strip . content . last . (assignee >=> child >=> hover >=> child) . fromDocument . parseLBS <$> L.readFile "xmlfile"
["This Value!"]