XML to Haskell Converter Online

Paste any XML and generate Haskell data declarations using Text, Int, Double, Bool, and lists — ready to bind via xml-conduit or hxt-pickle. Field names are prefixed with the record name to avoid duplicate-record-field clashes.

What is an XML to Haskell converter?

An XML to Haskell converter takes a real XML document and emits Haskell data declarations whose record fields mirror the XML element / attribute tree. The OpenFormatter generator infers Haskell types (Text, Int, Double, Bool, lists), prefixes field names with the record name to dodge duplicate-record-field clashes, and produces a complete data hierarchy ready for xml-conduit or hxt-pickle.

XML differs from JSON in two ways your Haskell model has to honour. First, XML has attributes on elements (<book id="bk101">) — these are read from the elementAttributes map in xml-conduit, not from elementNodes. The generator suffixes attribute fields with Attr. Second, XML allows arbitrary repetition; the generator emits list-typed fields for repeated children.

Sample XML and the Haskell data it generates

Input XML

<book id="bk101" lang="en">
  <title>Real World Haskell</title>
  <year>2008</year>
  <price currency="USD">39.99</price>
</book>

Generated Haskell

{-# LANGUAGE OverloadedStrings #-}

module Generated where

import Data.Text (Text)

data Price = Price
  { priceCurrencyAttr :: Text
  , priceTextValue    :: Double
  } deriving (Show, Eq)

data Book = Book
  { bookIdAttr   :: Text
  , bookLangAttr :: Text
  , bookTitle    :: Text
  , bookYear     :: Int
  , bookPrice    :: Price
  } deriving (Show, Eq)

Notice bookIdAttr and bookLangAttr carry both the record-name prefix and the Attr suffix. The nested <price currency="USD"> generates a Price data declaration with one attribute and one text-content field — exactly what xml-conduit exposes via elementAttributes and elementNodes.

How to convert XML to Haskell — 4 steps

  1. Paste your XML. A SOAP envelope body, an RSS feed entry, an enterprise XML response — anything well-formed.
  2. Click Convert. The browser parses the XML and generates Haskell data declarations for the full element tree.
  3. Wrap optionals in Maybe. Mark fields that can be absent as Maybe a — the generator stays neutral.
  4. Write the xml-conduit decoder. Use Text.XML.Cursor to walk the Document and populate the generated record.

Pure Records

Every record uses Text, Int, Double, Bool, or [a] — Haskell list — and derives Show, Eq for cheap debugging and equality checks.

Per-record Prefixes

Field names start with the lowercase record name to dodge the Haskell shared-namespace duplicate-record-field error without needing OverloadedRecordDot.

Client-Side Only

Your XML — including SOAP responses with credentials — is parsed in JavaScript on your machine. Verify in DevTools: zero network requests on Convert.

Common use cases

  • check_circleGenerate Haskell records from a SOAP service response
  • check_circleBuild Haskell models for an RSS or Atom feed reader
  • check_circleConvert configuration XML to typed Haskell data declarations
  • check_circleGenerate Haskell types from an XSD-described XML file
  • check_circleCreate xml-conduit decoders for legacy enterprise XML
  • check_circleBuild typed data for SAML or WS-Security XML payloads
  • check_circleGenerate Haskell models for sitemap.xml or robots.xml
  • check_circleConvert Stack / Cabal-adjacent XML descriptors to typed data

Why client-side conversion matters

SOAP responses and enterprise XML often contain customer PII, API tokens, internal endpoint names, and database identifiers. Pasting them into a server-hosted converter is a compliance violation in most regulated industries. OpenFormatter parses the XML and generates Haskell source entirely in JavaScript on your device — no upload, no logs, no cookies. Open DevTools → Network and confirm no requests fire when you click Convert.

More XML tooling

Validate, format, or convert XML to other languages — every tool runs locally in your browser.

Frequently Asked Questions

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.

XML to Haskell Converter Online — Free Data Generator