xml-conduit vs hxt — which should I use?
xml-conduit is the recommended modern choice — fast streaming parser, integrates with the conduit ecosystem, and Snoyman maintains it actively. It uses Text natively and has a Document type with explicit attribute / child accessors. hxt (Haskell XML Toolbox) is older, supports HXT picklers for declarative XML-to-record binding, and ships XPath / XSLT support — but it carries a heavier dependency tree. For new code: xml-conduit. For declarative pickling: hxt-pickle. The generated record works with both.
How are XML attributes mapped to Haskell record fields?
Attribute fields are suffixed with "Attr" and prefixed with the record name (Haskell record selectors live in the same namespace, so prefixes prevent collisions across data types). For <book id="bk101"> the field becomes bookIdAttr :: Text. With xml-conduit you populate it from the Document attribute map; with hxt-pickle you write a custom pickler.
Why prefix every field with the record name?
Without DuplicateRecordFields enabled, two records with the same field name (data Book { id :: Text } vs data Author { id :: Text }) cause a name clash because record selectors share the module namespace. Prefixing with the record name (bookId, authorId) is the conservative idiom that works everywhere. Enable DuplicateRecordFields and OverloadedRecordDot if you want bare names with disambiguation.
Why Text instead of String?
String in Haskell is [Char] — a linked list of characters with terrible performance for anything beyond toy data. Text (from the text package) is a packed UTF-16 array and is the universally recommended replacement. xml-conduit uses Text natively, and the generator emits OverloadedStrings so you can still write literals as plain "..." in your code.
How are repeated elements like <tag> typed?
Repeated child elements become [a] (Haskell list) fields with the element type inferred from the first occurrence. Lists in Haskell are lazy linked lists — fine for streaming but consider Data.Vector for thousands of elements. For sequence-aware parsing with xml-conduit use streamable: it folds incrementally without materialising the whole list.
Does the generator emit instance ToXml / FromXml?
No — only the data declarations. The downstream parser dictates the typeclass: xml-conduit needs Cursor traversals, hxt-pickle needs XmlPickler instances, and xeno needs explicit decoder functions. The generator gives you the success-side data shape; you write the parser bindings.
Is the XML I paste sent to your servers?
No. XML is parsed by the browser DOMParser and the Haskell source is generated entirely in JavaScript on your machine. Open DevTools → Network and you will see no requests when you click Convert. Safe for SOAP responses and configuration files containing API keys or credentials.
How do I parse XML into the generated record with xml-conduit?
Use Text.XML.Cursor: doc <- Text.XML.readFile def "book.xml"; let cur = fromDocument doc in Book <$> (cur $| attribute "id" >>> head) <*> (cur $/ element "title" &/ content >>> head) — combinators stay in Cursor monad form. For declarative pickling use Text.XML.HXT.Arrow.Pickle and write xpickle :: PU Book.