HaXml-1.25.3: Utilities for manipulating XML documents

Safe Haskell | Safe-Inferred
Language | Haskell98

Text.XML.HaXml.Combinators

Contents

- The content filter type.
- Simple filters.
  - Selection filters.
  - Predicate filters.
  - Search filters.
- Filter combinators
  - Basic combinators.
  - Recursive search.
  - Interior editing.
  - Constructive filters.
- C-like conditionals.
- Filters with labelled results.
  - Using and combining labelled filters.
  - Some label-generating filters.

Description

This module defines the notion of filters and filter combinators for processing XML documents.

These XML transformation combinators are described in the paper ``Haskell and XML: Generic Combinators or Type-Based Translation?'' Malcolm Wallace and Colin Runciman, Proceedings ICFP'99.

Synopsis

- type CFilter i = Content i -> [Content i]
- keep :: a -> [a]
- none :: a -> [b]
- children :: CFilter i
- childrenBy :: CFilter i -> CFilter i
- position :: Int -> CFilter i -> CFilter i
- elm :: CFilter i
- txt :: CFilter i
- tag :: String -> CFilter i
- attr :: String -> CFilter i
- attrval :: Attribute -> CFilter i
- tagWith :: (String -> Bool) -> CFilter i
- find :: String -> (String -> CFilter i) -> CFilter i
- iffind :: String -> (String -> CFilter i) -> CFilter i -> CFilter i
- ifTxt :: (String -> CFilter i) -> CFilter i -> CFilter i
- o :: CFilter i -> CFilter i -> CFilter i
- union :: (a -> [b]) -> (a -> [b]) -> a -> [b]
- cat :: [a -> [b]] -> a -> [b]
- andThen :: (a -> c) -> (c -> a -> b) -> a -> b
- (|>|) :: (a -> [b]) -> (a -> [b]) -> a -> [b]
- with :: CFilter i -> CFilter i -> CFilter i
- without :: CFilter i -> CFilter i -> CFilter i
- (/>) :: CFilter i -> CFilter i -> CFilter i
- (</) :: CFilter i -> CFilter i -> CFilter i
- et :: (String -> CFilter i) -> CFilter i -> CFilter i
- path :: [CFilter i] -> CFilter i
- deep :: CFilter i -> CFilter i
- deepest :: CFilter i -> CFilter i
- multi :: CFilter i -> CFilter i
- when :: CFilter i -> CFilter i -> CFilter i
- guards :: CFilter i -> CFilter i -> CFilter i
- chip :: CFilter i -> CFilter i
- inplace :: CFilter i -> CFilter i
- recursivelyInPlace :: CFilter i -> CFilter i
- foldXml :: CFilter i -> CFilter i
- mkElem :: String -> [CFilter i] -> CFilter i
- mkElemAttr :: String -> [(String, CFilter i)] -> [CFilter i] -> CFilter i
- literal :: String -> CFilter i
- cdata :: String -> CFilter i
- replaceTag :: String -> CFilter i
- replaceAttrs :: [(String, String)] -> CFilter i
- addAttribute :: String -> String -> CFilter a
- data ThenElse a = a :> a
- (?>) :: (a -> [b]) -> ThenElse (a -> [b]) -> a -> [b]
- type LabelFilter i a = Content i -> [(a, Content i)]
- oo :: (a -> CFilter i) -> LabelFilter i a -> CFilter i
- x :: (CFilter i -> LabelFilter i a) -> (CFilter i -> LabelFilter i b) -> CFilter i -> LabelFilter i (a, b)
- numbered :: CFilter i -> LabelFilter i Int
- interspersed :: String -> CFilter i -> String -> LabelFilter i String
- tagged :: CFilter i -> LabelFilter i String
- attributed :: String -> CFilter i -> LabelFilter i String
- textlabelled :: CFilter i -> LabelFilter i (Maybe String)
- extracted :: (Content i -> a) -> CFilter i -> LabelFilter i a

# The content filter type.

type CFilter i = Content i -> [Content i]

All document transformations are content filters. A filter takes a single XML `Content` value and returns a sequence of `Content` values, possibly empty.

# Simple filters.

## Selection filters.

In the algebra of combinators, `none` is the zero, and `keep` the identity. (They have a more general type than just CFilter.)

keep :: a -> [a]

none :: a -> [b]

children :: CFilter i

Throw away current node, keep just the (unprocessed) children.

childrenBy :: CFilter i -> CFilter i

Process children using specified filters.

position :: Int -> CFilter i -> CFilter i

Select the `n`'th positional result of a filter.

## Predicate filters.

These filters either keep or throw away some content based on a simple test. For instance, `elm` keeps only a tagged element, `txt` keeps only non-element text, `tag` keeps only an element with the named tag, `attr` keeps only an element with the named attribute, `attrval` keeps only an element with the given attribute value, `tagWith` keeps only an element whose tag name satisfies the given predicate.

elm :: CFilter i

txt :: CFilter i

tag :: String -> CFilter i

attr :: String -> CFilter i

attrval :: Attribute -> CFilter i

tagWith :: (String -> Bool) -> CFilter i

## Search filters.

find :: String -> (String -> CFilter i) -> CFilter i

For a mandatory attribute field, `find key cont` looks up the value of the attribute name `key`, and applies the continuation `cont` to the value.

iffind :: String -> (String -> CFilter i) -> CFilter i -> CFilter i

When an attribute field may be absent, use `iffind key yes no` to lookup its value. If the attribute is absent, it acts as the `no` filter, otherwise it applies the `yes` filter.

ifTxt :: (String -> CFilter i) -> CFilter i -> CFilter i

`ifTxt yes no` processes any textual content with the `yes` filter, but otherwise is the same as the `no` filter.

# Filter combinators

## Basic combinators.

o :: CFilter i -> CFilter i -> CFilter i infixr 5

Sequential (Irish,backwards) composition

union :: (a -> [b]) -> (a -> [b]) -> a -> [b] infixr 5

Binary parallel composition. Each filter uses a copy of the input, rather than one filter using the result of the other. (Has a more general type than just CFilter.)

cat :: [a -> [b]] -> a -> [b]

Glue a list of filters together. (A list version of union; also has a more general type than just CFilter.)

andThen :: (a -> c) -> (c -> a -> b) -> a -> b infixr 5

A special form of filter composition where the second filter works over the same data as the first, but also uses the first's result.

(|>|) :: (a -> [b]) -> (a -> [b]) -> a -> [b] infixl 5

Directional choice: in `f |>| g` give g-productions only if no f-productions

with :: CFilter i -> CFilter i -> CFilter i infixl 6

Pruning: in `f `with` g`, keep only those f-productions which have at least one g-production

without :: CFilter i -> CFilter i -> CFilter i infixl 6

Pruning: in `f `without` g`, keep only those f-productions which have no g-productions

(/>) :: CFilter i -> CFilter i -> CFilter i infixl 5

Pronounced slash, `f /> g` means g inside f

(</) :: CFilter i -> CFilter i -> CFilter i infixl 5

Pronounced outside, `f </ g` means f containing g

et :: (String -> CFilter i) -> CFilter i -> CFilter i

Join an element-matching filter with a text-only filter

path :: [CFilter i] -> CFilter i

Express a list of filters like an XPath query, e.g. `path [children, tag "name1", attr "attr1", children, tag "name2"]` is like the XPath query `/name1[@attr1]/name2`.

## Recursive search.

Recursive search has three variants: `deep` does a breadth-first search of the tree, `deepest` does a depth-first search, `multi` returns content at all tree-levels, even those strictly contained within results that have already been returned.

deep :: CFilter i -> CFilter i

deepest :: CFilter i -> CFilter i

multi :: CFilter i -> CFilter i

## Interior editing.

when :: CFilter i -> CFilter i -> CFilter i infixr 4

Interior editing: `f `when` g` applies `f` only when the predicate `g` succeeds, otherwise the content is unchanged.

guards :: CFilter i -> CFilter i -> CFilter i infixr 4

Interior editing: `g `guards` f` applies `f` only when the predicate `g` succeeds, otherwise the content is discarded.

chip :: CFilter i -> CFilter i

Process CHildren In Place. The filter is applied to any children of an element content, and the element rebuilt around the results.

inplace :: CFilter i -> CFilter i

Process an element In Place. The filter is applied to the element itself, and then the original element rebuilt around the results.

recursivelyInPlace :: CFilter i -> CFilter i

Recursively process an element in place. That is, the filter is applied to the element itself, then recursively to the results of the filter, all the way to the bottom, then the original element rebuilt around the final results.

foldXml :: CFilter i -> CFilter i

Recursive application of filters: a fold-like operator. Defined as `f `o` chip (foldXml f)`.

## Constructive filters.

The constructive filters are primitive filters for building new elements, or editing existing elements.

mkElem :: String -> [CFilter i] -> CFilter i

Build an element with the given tag name - its content is the results of the given list of filters.

mkElemAttr :: String -> [(String, CFilter i)] -> [CFilter i] -> CFilter i

Build an element with the given name, attributes, and content.

literal :: String -> CFilter i

Build some textual content.

cdata :: String -> CFilter i

Build some CDATA content.

replaceTag :: String -> CFilter i

Rename an element tag (leaving attributes in place).

replaceAttrs :: [(String, String)] -> CFilter i

Replace the attributes of an element (leaving tag the same).

addAttribute :: String -> String -> CFilter a

Add the desired attribute name and value to the topmost element, without changing the element in any other way.

# C-like conditionals.

These definitions provide C-like conditionals, lifted to the filter level.

The `(cond ? yes : no)` style in C becomes `(cond ?> yes :> no)` in Haskell.

data ThenElse a

Conjoin the two branches of a conditional.

Constructors

a :> a infixr 3 |

(?>) :: (a -> [b]) -> ThenElse (a -> [b]) -> a -> [b] infixr 3

Select between the two branches of a joined conditional.

# Filters with labelled results.

type LabelFilter i a = Content i -> [(a, Content i)]

A LabelFilter is like a CFilter except that it pairs up a polymorphic value (label) with each of its results.

## Using and combining labelled filters.

oo :: (a -> CFilter i) -> LabelFilter i a -> CFilter i infixr 5

Compose a label-processing filter with a label-generating filter.

x :: (CFilter i -> LabelFilter i a) -> (CFilter i -> LabelFilter i b) -> CFilter i -> LabelFilter i (a, b)

Combine labels. Think of this as a pair-wise zip on labels. e.g. `(numbered `x` tagged)`

## Some label-generating filters.

numbered :: CFilter i -> LabelFilter i Int

Number the results from 1 upwards.

interspersed :: String -> CFilter i -> String -> LabelFilter i String

In `interspersed a f b`, label each result of `f` with the string `a`, except for the last one which is labelled with the string `b`.

tagged :: CFilter i -> LabelFilter i String

Label each element in the result with its tag name. Non-element results get an empty string label.

attributed :: String -> CFilter i -> LabelFilter i String

Label each element in the result with the value of the named attribute. Elements without the attribute, and non-element results, get an empty string label.

textlabelled :: CFilter i -> LabelFilter i (Maybe String)

Label each textual part of the result with its text. Element results get an empty string label.

extracted :: (Content i -> a) -> CFilter i -> LabelFilter i a

Label each content with some information extracted from itself.