Domanda

Tutti gli esempi che ho visto finora usando il toolkit XML Haskell, HXT, usi runX per eseguire il parser. corre runX all'interno del IO monade. C'è un modo di utilizzare questo parser XML al di fuori di IO? Sembra essere un'operazione di puro a me, non capisco il motivo per cui sono costretto a essere dentro IO.

È stato utile?

Soluzione

È possibile utilizzare di HXT xread insieme a runLA per analizzare un esterno stringa XML di IO.

xread ha il tipo seguente:

xread :: ArrowXml a => a String XmlTree

Questo significa che è possibile comporre con qualsiasi freccia di tipo (ArrowXml a) => a XmlTree Whatever per ottenere un a String Whatever.

runLA è come runX, ma per le cose di tipo LA:

runLA :: LA a b -> a -> [b]

LA è un'istanza di ArrowXml.

Per mettere tutto questo insieme, la seguente versione di la mia risposta alla tua domanda precedente utilizza HXT per analizzare una stringa contenente XML ben formato senza alcuna IO coinvolti:

{-# LANGUAGE Arrows #-}
module Main where

import qualified Data.Map as M
import Text.XML.HXT.Arrow

classes :: (ArrowXml a) => a XmlTree (M.Map String String)
classes = listA (divs >>> pairs) >>> arr M.fromList
  where
    divs = getChildren >>> hasName "div"
    pairs = proc div -> do
      cls <- getAttrValue "class" -< div
      val <- deep getText         -< div
      returnA -< (cls, val)

getValues :: (ArrowXml a) => [String] -> a XmlTree (String, Maybe String)
getValues cs = classes >>> arr (zip cs . lookupValues cs) >>> unlistA
  where lookupValues cs m = map (flip M.lookup m) cs

xml = "<div><div class='c1'>a</div><div class='c2'>b</div>\
      \<div class='c3'>123</div><div class='c4'>234</div></div>"

values :: [(String, Maybe String)]
values = runLA (xread >>> getValues ["c1", "c2", "c3", "c4"]) xml

main = print values

classes e getValues sono simili alla versione precedente, con qualche piccola modifica per soddisfare l'input e l'output previsto. La differenza principale è che qui usiamo xread e runLA invece di readString e runX.

Sarebbe bello per essere in grado di leggere qualcosa di simile a un ByteString pigro in modo simile, ma per quanto ne so questo non è attualmente possibile con HXT.


Un paio di altre cose: si possono stringhe parse in questo modo senza IO, ma probabilmente è meglio usare runX ogni volta che è possibile: ti dà più controllo sulla configurazione del parser, errore messaggi, ecc.

Inoltre: ho cercato di rendere il codice nell'esempio semplice e facile da estendere, ma i combinatori di Control.Arrow e Control.Arrow.ArrowList permettono di lavorare con le frecce molto più conciso, se vi piace. La seguente è una definizione equivalente di classes, ad esempio:

classes = (getChildren >>> hasName "div" >>> pairs) >. M.fromList
  where pairs = getAttrValue "class" &&& deep getText

Altri suggerimenti

La risposta di Travis Brown è stato molto utile. Voglio solo aggiungere la mia soluzione qui, che credo sia un po 'più generale (con le stesse funzioni, solo ignorando i problemi problema-specifiche).

Sono stato in precedenza deserializzazione con:

upIO      :: XmlPickler a => String -> IO [a]
upIO str   = runX $ readString [] str >>> arrL (maybeToList . unpickleDoc xpickle)

che sono stato in grado di cambiare a questo:

upPure    :: XmlPickler a => String -> [a]
upPure str = runLA (xreadDoc >>> arrL (maybeToList . unpickleDoc xpickle)) str

Sono completamente d'accordo con lui che facendo questo ti dà meno controllo sulla configurazione del parser ecc, che è un peccato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top