xmlgen: Generating mixed content XML nodes (i.e. with both body text and nested XML elements) in Haskell

StackOverflow https://stackoverflow.com/questions/14495039

  •  17-01-2022
  •  | 
  •  

Question

I am trying to generate XML with mixed content nodes (has both body text and children nodes), like the following:

<person>
some text
<hugo age="24">thingy</hugo>
</person>

I am using the xmlgen library.

Here's how far I got:

import Text.XML.Generator
import qualified Data.ByteString as B

makeElt = xelem "person" $
            xelems $ (xtext "some text" : 
                      (xelem "hugo" (xattr "age" "24" <#> 
                                     xtext "thingy")))

main = do
  let elt = makeElt
  B.putStr . xrender $ doc defaultDocInfo elt

This does not compile and GHC's error message is incomprehensible to me (as a beginner):

$ runhaskell test.hs

test.hs:6:24:
    Couldn't match type `Elem' with `Xml Elem'
    The function `xelem' is applied to two arguments,
    but its type `[Char]
                  -> Text.XML.Generator.MkElemRes [Char] (Xml Attr, Xml Elem)'
    has only one
    In the second argument of `(:)', namely
      `(xelem "hugo" (xattr "age" "24" <#> xtext "thingy"))'
    In the second argument of `($)', namely
      `(xtext "some text"
        : (xelem "hugo" (xattr "age" "24" <#> xtext "thingy")))'

test.hs:6:24:
    Couldn't match type `Xml' with `[]'
    The function `xelem' is applied to two arguments,
    but its type `[Char]
                  -> Text.XML.Generator.MkElemRes [Char] (Xml Attr, Xml Elem)'
    has only one
    In the second argument of `(:)', namely
      `(xelem "hugo" (xattr "age" "24" <#> xtext "thingy"))'
    In the second argument of `($)', namely
      `(xtext "some text"
        : (xelem "hugo" (xattr "age" "24" <#> xtext "thingy")))'

Am I quite close to the result that I need, or should I be writing this differently?

I tried looking for examples usage of the xmlgen library but didn't find my use-case. Any help is greatly appreciated.

Was it helpful?

Solution

The problem is in the expression

(xtext "some text" : 
 (xelem "hugo" (xattr "age" "24" <#> 
                xtext "thingy")))

The part after the : (starting with xelem "hugo") is not a list. I good way to debug such type problems is using ghci. That's what I did in the first place, and the ghci quickly leads you in the right direction:

*Text.XML.Generator> :t xtext "some text" : xelem "hugo" (xattr "age" "24" <#> xtext "thingy")

<interactive>:1:21:
    Couldn't match expected type `[Xml Elem]'
                with actual type `Xml Elem'
    In the return type of a call of `xelem'
    In the second argument of `(:)', namely
      `xelem "hugo" (xattr "age" "24" <#> xtext "thingy")'
    In the expression:
      xtext "some text"
      : xelem "hugo" (xattr "age" "24" <#> xtext "thingy")

A good question is why GHC give's such a bad error message in the first place. The problem is that the result type of xelem is overloaded, in fact xelem has type n -> MkElemRes n c. The instantiation for MkElemRes n c you are trying to use in your example is indeed a function type, so this part of your example is correct. But in general MkElemRes n c does not need to be a function type and that's why GHC complains about two arguments where it expects only one (namely one of type MkElemRes n c).

Here are one solution to your original problem:

xelem "person" $ 
   xelems [xtext "some text", xelem "hugo" (xattr "age" "24" <#> xtext "thingy")]

Here is an alternative solution:

xelem "person" $ xtext "some text" <> xelem "hugo" (xattr "age" "24" <#> xtext "thingy")
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top