【问题标题】:Get all Names from xml-conduit从 xml-conduit 获取所有名称
【发布时间】:2014-03-30 18:51:50
【问题描述】:

我正在解析来自 http://hackage.haskell.org/package/xml-conduit-1.1.0.9/docs/Text-XML-Stream-Parse.html 的修改后的 XML

它是这样的:

<?xml version="1.0" encoding="utf-8"?>
<population xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://example.com">
  <success>true</success>
  <row_count>2</row_count>
  <summary>
    <bananas>0</bananas>
  </summary>
  <people>
      <person>
          <firstname>Michael</firstname>
          <age>25</age>
      </person>
      <person>
          <firstname>Eliezer</firstname>
          <age>2</age>
      </person>
  </people>
</population>

如何获取每个人的firstnameage 列表?

我的目标是使用http-conduit下载这个xml然后解析它,但我正在寻找一个解决方案,当没有属性时如何解析(使用tagNoAttrs?)

这是我尝试过的,我在 Haskell cmets 中添加了我的问题:

{-# LANGUAGE OverloadedStrings #-}
import Control.Monad.Trans.Resource
import Data.Conduit (($$))
import Data.Text (Text, unpack)
import Text.XML.Stream.Parse
import Control.Applicative ((<*))

data Person = Person Int Text
        deriving Show

-- Do I need to change the lambda function \age to something else to get both name and age?
parsePerson = tagNoAttr "person" $ \age -> do
        name <- content  -- How do I get age from the content?  "unpack" is for attributes
        return $ Person age name

parsePeople = tagNoAttr "people" $ many parsePerson

-- This doesn't ignore the xmlns attributes
parsePopulation  = tagName "population" (optionalAttr "xmlns" <* ignoreAttrs) $ parsePeople

main = do
        people <- runResourceT $
             parseFile def "people2.xml" $$ parsePopulation
        print people

【问题讨论】:

  • 已编辑以添加我迄今为止尝试过的内容和 cmets

标签: xml haskell xml-conduit


【解决方案1】:

首先:在 xml-conduit 中解析组合子已经很长时间没有更新了,并且显示它们的年龄。我建议大多数人改用 DOM 或光标界面。也就是说,让我们看看你的例子。您的代码有两个问题:

  • 它不能正确处理 XML 命名空间。所有元素名称都在 http://example.com 命名空间中,您的代码需要反映这一点。
  • 解析组合器要求您考虑所有元素。它们不会自动为您跳过某些元素。

所以这是一个使用流式 API 的实现,可以得到所需的结果:

{-# LANGUAGE OverloadedStrings #-}
import           Control.Monad.Trans.Resource (runResourceT)
import           Data.Conduit                 (Consumer, ($$))
import           Data.Text                    (Text)
import           Data.Text.Read               (decimal)
import           Data.XML.Types               (Event)
import           Text.XML.Stream.Parse

data Person = Person Int Text
        deriving Show

-- Do I need to change the lambda function \age to something else to get both name and age?
parsePerson :: MonadThrow m => Consumer Event m (Maybe Person)
parsePerson = tagNoAttr "{http://example.com}person" $ do
        name <- force "firstname tag missing" $ tagNoAttr "{http://example.com}firstname" content
        ageText <- force "age tag missing" $ tagNoAttr "{http://example.com}age" content
        case decimal ageText of
            Right (age, "") -> return $ Person age name
            _ -> force "invalid age value" $ return Nothing

parsePeople :: MonadThrow m => Consumer Event m [Person]
parsePeople = force "no people tag" $ do
    _ <- tagNoAttr "{http://example.com}success" content
    _ <- tagNoAttr "{http://example.com}row_count" content
    _ <- tagNoAttr "{http://example.com}summary" $
        tagNoAttr "{http://example.com}bananas" content
    tagNoAttr "{http://example.com}people" $ many parsePerson

-- This doesn't ignore the xmlns attributes
parsePopulation :: MonadThrow m => Consumer Event m [Person]
parsePopulation = force "population tag missing" $
    tagName "{http://example.com}population" ignoreAttrs $ \() -> parsePeople

main :: IO ()
main = do
        people <- runResourceT $
             parseFile def "people2.xml" $$ parsePopulation
        print people

这是一个使用游标 API 的示例。请注意,它具有不同的错误处理特性,但对于格式正确的输入应该产生相同的结果。

{-# LANGUAGE OverloadedStrings #-}
import Text.XML
import Text.XML.Cursor
import Data.Text (Text)
import Data.Text.Read (decimal)
import Data.Monoid (mconcat)

main :: IO ()
main = do
    doc <- Text.XML.readFile def "people2.xml"
    let cursor = fromDocument doc
    print $ cursor $// element "{http://example.com}person" >=> parsePerson

data Person = Person Int Text
        deriving Show

parsePerson :: Cursor -> [Person]
parsePerson c = do
    let name = c $/ element "{http://example.com}firstname" &/ content
        ageText = c $/ element "{http://example.com}age" &/ content
    case decimal $ mconcat ageText of
        Right (age, "") -> [Person age $ mconcat name]
        _ -> []

【讨论】:

  • 感谢您提供的两种方法!游标 API 看起来要简单得多。如果我使用 http-conduit 进行 POST(这就是我获取 xml 的方式),我是否需要继续使用 xml-conduit 还是可以使用 cursor API?我在 http-conduit 中使用 httpLbs(惰性字节字符串)
  • 好吧,您仍将使用 xml-conduit,因为光标 API 是其中的一部分。最有效的方法是将sinkDochttp 函数一起使用。虽然您可以走更简单的路线,如果您愿意,只需使用httpLbs
  • 游标 API 是否仍然避免一次将整个 XML 结构保存在内存中?没关系,好​​像是这样:stackoverflow.com/questions/29454267/…
猜你喜欢
  • 1970-01-01
  • 2019-10-02
  • 1970-01-01
  • 2010-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-03
相关资源
最近更新 更多