3[](https://travis-ci.org/typeable/xml-isogen)
5TemplateHaskell generators for XML-isomorphic data types, including
6instances for parsing and rendering. A convenient DSL to define those
9Essentially it's a haskell DSL which allows its users to generate XML parsers and generators for haskell datatypes.
10See also [xsd-isogen](https://github.com/typeable/xsd-isogen)
14Lets go through series of examples. First things first:
17{-# LANGUAGE TemplateHaskell #-}
18{-# LANGUAGE OverloadedStrings #-}
19{-# LANGUAGE DeriveGeneric #-}
24import Prelude hiding ((^))
25import Control.DeepSeq -- from deepseq
27import Data.THGen.XML -- from xml-isogen
28import Text.XML.Writer -- from xml-conduit-writer
29import Text.XML.DOM.Parser -- from dom-parser
34Lets say we want to parse and/or generate an XML file of the following form:
37<?xml version="1.0" encoding="UTF-8"?>
48Person has a name and an email, and email could be omitted. With `xml-isogen` it's
49enough to write the following definition:
52"Person" =:= record ParserAndGenerator
57At this point you can load the module into `ghci` and check what was generated for us
63 = XmlPerson {_xpName :: !Text, _xpEmail :: !(Maybe Text)}
65 lens-4.18.1:Control.Lens.Type.Lens' XmlPerson (Maybe Text)
66xpName :: lens-4.18.1:Control.Lens.Type.Lens' XmlPerson Text
69We have a data type `XmlPerson` with two fields and two lenses. Note that the
70fields have a prefix built of an underscore and all upper case characters
71and digits of the type name.
74Lets take a closer look at `XmlPerson`:
77*Tutorial> :i XmlPerson
79 = XmlPerson {_xpName :: !Text, _xpEmail :: !(Maybe Text)}
80 -- Defined at Tutorial.hs:13:1
81instance Show XmlPerson -- Defined at Tutorial.hs:13:1
82instance Eq XmlPerson -- Defined at Tutorial.hs:13:1
83instance FromDom XmlPerson -- Defined at Tutorial.hs:13:1
84instance ToXML XmlPerson -- Defined at Tutorial.hs:13:1
89[FromDom](https://hackage.haskell.org/package/dom-parser-3.1.0/docs/Text-XML-DOM-Parser-FromDom.html#t:FromDom)
91[ToXML](https://hackage.haskell.org/package/xml-conduit-writer-0.1.1.2/docs/Text-XML-Writer.html#t:ToXML)
92instance generated for us. That's because
93we instructed `xml-isogen` to generate them using the `ParserAndGenerator` noun. You can specify
94also `Parser` or `Generator` if you want only one of them.
96The `_xpEmail` field is optional; that's because we prefixed it with `?` modifier.
97Here is the list of possible modifiers that affect types:
99Modifier | Description | Generated Type
102`?` | optional | `Maybe a`
103`*` | repeated | `[a]`
104`+` | nonempty | `NonEmpty a`
108Fields in a record may have any types as long as they are instances of `Eq`, `Show`,
109`NFData`, `FromDom` (for the parser) and `ToXml` (for the generator). Remember
110though that TemplateHaskell requires types to be available before they are used
113You can omit field types altogether, in that case the type will be assumed to be a
114capitalized field name with an `Xml` prefix.
115It's your responsibility to make sure that type exists.
119newtype XmlEmail = XmlEmail Text
120 deriving (Eq, Show, NFData, ToXML)
122instance FromDom XmlEmail where
123 fromDom = XmlEmail <$> fromDom
125"Example1" =:= record Parser
126 ! "email" -- will have type XmlEmail
131Often XML element can contain only limited number of possible values. Lets define
132a type `Status` that can have only values `Active`, `Pending` or `Deleted`:
135"Status" =:= enum ParserAndGenerator
141This definition will generate the following type for us:
148 | UnknownXmlStatus !String
151It has all the necessary instances, so you can use it as a type for a field.
155Sometimes the XML you are dealing with contains a mix of elements and immediate
156content. Something like the following:
159<?xml version="1.0" encoding="UTF-8"?>
171You can model this with an "append content" modifier `^`. It will instruct
172`xml-isogen` to append content of the field as it is, without wrapping it
173into an XML element. For our case it may look like this:
176"Example2" =:= record ParserAndGenerator
182After parsing the XML above, we'll get the following:
185XmlExample2 {_xe2Field1 = "I am", _xe2Mixed = "totally", _xe2Field2 = "weird"}
190`xml-isogen` also supports XML attributes using `!%` and `?%` modifiers:
193"Example3" =:= record ParserAndGenerator
195 !% "attribute1" [t|Text|]
196 ?% "attribute2" [t|Text|]
198"Body" =:= record ParserAndGenerator
199 ! "root" [t|XmlExample3|]
202The following two types will be generated:
206 = XmlExample3 {_xe3Field1 :: !Text,
207 _xe3Attribute1 :: !Text,
208 _xe3Attribute2 :: !(Maybe Text)}
210newtype XmlBody = XmlBody {_xbRoot :: XmlExample3}
213The `_xe3Attribute2` is optional because we used `?%` modifier. Attributes will be
214attached to parent XML element. Here is an example of the generated XML file:
217<root attribute1="world">
224Note that attributes are attached to the parent XML element, that's why we needed
227And here is what you get after parsing the XML:
230XmlExample3 {_xe3Field1 = "hello", _xe3Attribute1 = "world", _xe3Attribute2 = Nothing}
235Often XSD schema requires XML elements to be qualified with a namespace. To instruct
236`xml-isogen` to qualify fields, specify namespace is a curly brackets:
239"Example4" =:= record ParserAndGenerator
241 ! "{http://example.com/1}field2" [t|Text|]
242 ! "{http://example.com/2}field3" [t|Text|]
245Here is the generated XML:
251<field2 xmlns="http://example.com/1">
254<field3 xmlns="http://example.com/2">
261Sometimes optional element in XML are encoded using `nil="true"` attribute instead of
262omitting the element. (The `nil` attribute comes from `http://www.w3.org/2001/XMLSchema-instance` namespace). With `xml-isogen` you handle it using the `Nillable` type:
265"Example5" =:= record ParserAndGenerator
266 ! "field" [t|Nillable Text|]
269If the field contains the value `Nothing`, like this
272XmlExample5 { _xe5Field = Nillable Nothing}
275then the following XML will be generated:
279 xmlns:ns="http://www.w3.org/2001/XMLSchema-instance"
286To start working with `xml-isogen` using nix use:
289nix-shell --packages cabal2nix --run "cabal2nix ." > default.nix
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list