2

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" 
Jonathan
  • 10,571
  • 13
  • 67
  • 103
  • Does this answer your question? [Which Haskell XML library to use?](https://stackoverflow.com/questions/1361307/which-haskell-xml-library-to-use) – sshine Jun 11 '20 at 23:14
  • What's unmaintainable about quasi-quotes? – Joseph Sible-Reinstate Monica Jun 11 '20 at 23:45
  • The problem is not a lack of Haskell XML libraries, it knowing how to use them to generate XML. For instance, I know I can generate `foo` using Lucid's `html_ "foo"`. But how can I do that for XML? – Jonathan Jun 12 '20 at 05:06
  • @JosephSible-ReinstateMonica Quasi-quotes themselves are fine. But using them as XML templates to generate XML has the problem that you lose composability. I can no longer abstract away frequently-used patterns, if I have to resort to this secondary language that can't be refactored in the same way as normal Haskell. – Jonathan Jun 12 '20 at 05:14
  • @SimonShine, I've edited my question to better reflect what I'm trying to ask. – Jonathan Jun 12 '20 at 05:27
  • @Jonathan: Sure, your revised question is more practical, but please edit questions in the future so that the answers given before you edited still make kind of sense. Otherwise we're on an egg hunt to make the Q match the A. :-D What I usually do is add "**Edit:** *Some one-liner saying what changed.*" or "`---` **Edit:** *A restatement of the question if it differs significantly.*" – sshine Jun 12 '20 at 07:39

2 Answers2

3

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.

K. A. Buhr
  • 45,621
  • 3
  • 45
  • 71
2

I often don't find sufficient examples to get easily started with new Haskell libraries.

So while the Q&A Which Haskell XML library to use? is from 2009 and the ecosystem has moved, and the answer might not be the same upon first inspection, this Q&A was the first hit for me when googling "Haskell XML site:stackoverflow.com", so it may be worthwhile to revise the answer. (Alternatively there's an option to close questions that ask for software recommendations, and presumably this is because the answer is both subjective and varies over time.)

If I search Hackage for "xml" and cross-reference the two lists, it seems that the candidates are mostly the same and that only one big contender seems to have entered, being xml-conduit. But rather than evaluate packages based on their "DLs" and how recent they were updated and so on (they seem somewhat equal), you could rely on the answer given:

  1. xml if your task is simple

There is only one example in the GitHub README:

{-# LANGUAGE RecordWildCards #-}
import Text.XML.Light

data Package = Package
  { pOrderNo  :: String
  , pOrderPos :: String
  , pBarcode  :: String
  , pNumber   :: String
  }

-- | Create XML from a Package
instance Node Package where
  node qn Package {..} =
    node qn
      [ unode "package_number" pNumber
      , unode "package_barcode" pBarcode
      , unode "order_number" pOrderNo
      , unode "order_position" pOrderPos
      ]

I went and looked for a library that uses the xml library for a better example. Doing a reverse dependency search for "xml", you get something like David Himmelstrup's reanimate-svg, which was just something that struck me as probably recent and probably a good, simple use. Here, Graphics.SvgTree covers parsing XML from a string and saving XML to a file, and Graphics.SvgTree.XmlParser covers converting to and from XML and some data type (in this case Document being an SVG).

sshine
  • 15,635
  • 1
  • 41
  • 66
  • 1
    This is helpful, but doesn't really answer my question, which is about how to generate XML. I've found plenty of libraries, but, due to what appears to be a lack of tutorials about how to use them, I'm at a loss for how to do something like in the example in my question. – Jonathan Jun 12 '20 at 05:17