Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I generate XML using Haskell?

Tags:

xml

haskell

I'm looking for a way to generate XML like in the example below, but the existing XML libraries don't provide adequate documentation for me to understand how to use them to generate XML. I can find plenty of XML-generating libraries, but none with a simple example which says: here's how to generate this XML.

The Blaze and Lucid libraries are great for generating HTML, for example. Let's say you want to make this HTML:

<emph class="foo">bar</emph>

Using Lucid, you would write:

emph_ [class_ "foo"] "bar" 

So what's a good way to do this with XML? I've been looking through the API documentation for, for instance, HaXml. But I can't find many tutorials about using those packages.

I did see that Yesod's Hamlet quasi-quoter is a very succinct way of generating XML. But I don't like the idea of quasi-quoting up a new schema, since it doesn't seem as maintable, and seems like learning a new language. So I'm hoping to find something more modular, and composable, like Blaze and Lucid.

Edit: To explain further, the problem is not a lack of Haskell XML libraries, or knowing which one to use. It's knowing how to use one (any of them) to generate XML. For instance, I know I can generate the HTML code <html>foo</html> using Lucid's html_ "foo". But how can I do that for XML?

Here's a pseudo-Haskell example of what I'm trying to do:

Objective: generate the following XML:

<foo attribute="something">
  <bar>
     <foobar>
        <barfoo>
           something here
        </barfoo>
     </foobar>
  </bar>
</foo>

Pseudo-Haskell:

foo_ [attribute_ "bar"] $ bar_ $ foobar_ $ barfoo_ "something here" 
like image 648
Jonathan Avatar asked Mar 05 '26 14:03

Jonathan


1 Answers

At the start of the Yesod Book documentation on xml-hamlet, they show how to generate XML using the Text.XML API from xml-conduit. So, literally, the "way you use (any of them) to generate XML" is:

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE OverloadedLists #-}

import qualified Data.Text.Lazy.IO as Text
import qualified Data.Map as Map
import Text.XML

myFoo = Element "foo" [("attribute", "something")]
  [ NodeElement $ Element "bar" []
    [ NodeElement $ Element "foobar" []
      [ NodeElement $ Element "barfoo" []
        [ NodeContent "something here" ]]]]

main = Text.putStrLn $ renderText def $ Document (Prologue [] Nothing []) myFoo []

giving:

> main
<?xml version="1.0" encoding="UTF-8"?><foo attribute="something">
<bar><foobar><barfoo>something here</barfoo></foobar></bar></foo>

The main issue is syntax, but it's really not that difficult to write your own lucid-like DSL using plain Haskell functions. If you want a completely flexible syntax that's schema agnostic, you could write:

-- helpers
attr_ = (,)
element_ nam attrs bdy = NodeElement $ Element nam (Map.fromList attrs) bdy
text_ = NodeContent

-- elements
foo_ = element_ "foo"
bar_ = element_ "bar"
foobar_ = element_ "foobar"
barfoo_ = element_ "barfoo_"

-- attributes
attribute_ = attr_ "attribute"

-- used to unwrap a top level Node to an Element
unNode (NodeElement x) = x

myFoo = unNode $
  foo_ [attribute_ "something"] $
    [ bar_ []
      [ foobar_ []
        [ barfoo_ []
          [ text_ "something here" ]]]]

main = Text.putStrLn $ renderText def $ Document (Prologue [] Nothing []) myFoo []

Usually the syntax can be simplified by considering your (informal) schema. If barfoos have no attributes and can only contain a block of text, then you write:

barfoo_ txt = element_ "barfoo" [] [text_ txt]

and use it as barfoo_ "something here" instead.

like image 192
K. A. Buhr Avatar answered Mar 07 '26 08:03

K. A. Buhr



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!