@@ -7,3 +7,276 @@ instances 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.
9Essentially it's a haskell DSL which allows its users to generate XML parsers and generators for haskell datatypes.
10+See also [xsd-isogen](https://github.com/typeable/xsd-isogen)
14+Lets go through series of examples. First things first:
17+{-# LANGUAGE TemplateHaskell #-}
18+{-# LANGUAGE OverloadedStrings #-}
19+{-# LANGUAGE DeriveGeneric #-}
24+import Prelude hiding ((^))
25+import Control.DeepSeq -- from deepseq
27+import Data.THGen.XML -- from xml-isogen
28+import Text.XML.Writer -- from xml-conduit-writer
29+import Text.XML.DOM.Parser -- from dom-parser
34+Lets say we want to parse and/or generate an XML file of the following form:
37+<?xml version="1.0" encoding="UTF-8"?>
48+Person has a name and an email, and email could be omitted. With `xml-isogen` it's
49+enough to write the following definition:
52+"Person" =:= record ParserAndGenerator
57+At 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)
66+xpName :: lens-4.18.1:Control.Lens.Type.Lens' XmlPerson Text
69+We have a data type `XmlPerson` with two fields and two lenses. Note that the
70+fields have a prefix built of an underscore and all upper case characters
71+and digits of the type name.
74+Lets take a closer look at `XmlPerson`:
77+*Tutorial> :i XmlPerson
79+ = XmlPerson {_xpName :: !Text, _xpEmail :: !(Maybe Text)}
80+ -- Defined at Tutorial.hs:13:1
81+instance Show XmlPerson -- Defined at Tutorial.hs:13:1
82+instance Eq XmlPerson -- Defined at Tutorial.hs:13:1
83+instance FromDom XmlPerson -- Defined at Tutorial.hs:13:1
84+instance 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)
92+instance generated for us. That's because
93+we instructed `xml-isogen` to generate them using the `ParserAndGenerator` noun. You can specify
94+also `Parser` or `Generator` if you want only one of them.
96+The `_xpEmail` field is optional; that's because we prefixed it with `?` modifier.
97+Here is the list of possible modifiers that affect types:
99+Modifier | Description | Generated Type
102+`?` | optional | `Maybe a`
103+`*` | repeated | `[a]`
104+`+` | nonempty | `NonEmpty a`
108+Fields 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
110+though that TemplateHaskell requires types to be available before they are used
113+You can omit field types altogether, in that case the type will be assumed to be a
114+capitalized field name with an `Xml` prefix.
115+It's your responsibility to make sure that type exists.
119+newtype XmlEmail = XmlEmail Text
120+ deriving (Eq, Show, NFData, ToXML)
122+instance FromDom XmlEmail where
123+ fromDom = XmlEmail <$> fromDom
125+"Example1" =:= record Parser
126+ ! "email" -- will have type XmlEmail
131+Often XML element can contain only limited number of possible values. Lets define
132+a type `Status` that can have only values `Active`, `Pending` or `Deleted`:
135+"Status" =:= enum ParserAndGenerator
141+This definition will generate the following type for us:
148+ | UnknownXmlStatus !String
151+It has all the necessary instances, so you can use it as a type for a field.
155+Sometimes the XML you are dealing with contains a mix of elements and immediate
156+content. Something like the following:
159+<?xml version="1.0" encoding="UTF-8"?>
171+You 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
173+into an XML element. For our case it may look like this:
176+"Example2" =:= record ParserAndGenerator
177+ ! "field1" [t|Text|]
179+ ! "field2" [t|Text|]
182+After parsing the XML above, we'll get the following:
185+XmlExample2 {_xe2Field1 = "I am", _xe2Mixed = "totally", _xe2Field2 = "weird"}
190+`xml-isogen` also supports XML attributes using `!%` and `?%` modifiers:
193+"Example3" =:= record ParserAndGenerator
194+ ! "field1" [t|Text|]
195+ !% "attribute1" [t|Text|]
196+ ?% "attribute2" [t|Text|]
198+"Body" =:= record ParserAndGenerator
199+ ! "root" [t|XmlExample3|]
202+The following two types will be generated:
206+ = XmlExample3 {_xe3Field1 :: !Text,
207+ _xe3Attribute1 :: !Text,
208+ _xe3Attribute2 :: !(Maybe Text)}
210+newtype XmlBody = XmlBody {_xbRoot :: XmlExample3}
213+The `_xe3Attribute2` is optional because we used `?%` modifier. Attributes will be
214+attached to parent XML element. Here is an example of the generated XML file:
217+<root attribute1="world">
224+Note that attributes are attached to the parent XML element, that's why we needed
227+And here is what you get after parsing the XML:
230+XmlExample3 {_xe3Field1 = "hello", _xe3Attribute1 = "world", _xe3Attribute2 = Nothing}
235+Often 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
240+ ! "field1" [t|Text|]
241+ ! "{http://example.com/1}field2" [t|Text|]
242+ ! "{http://example.com/2}field3" [t|Text|]
245+Here is the generated XML:
251+<field2 xmlns="http://example.com/1">
254+<field3 xmlns="http://example.com/2">
261+Sometimes optional element in XML are encoded using `nil="true"` attribute instead of
262+omitting 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|]
269+If the field contains the value `Nothing`, like this
272+XmlExample5 { _xe5Field = Nillable Nothing}
275+then the following XML will be generated:
279+ xmlns:ns="http://www.w3.org/2001/XMLSchema-instance"