XML to Haskell
Paste XML and instantly get Haskell record type definitions inferred from its structure.
Example
Input XML:
<book id="1">
<title>Real World Haskell</title>
<year>2008</year>
</book>
Generated Haskell:
data Root = Root
{ rootId :: Int
, rootTitle :: Text
, rootYear :: Int
} deriving (Show, Eq)How it works
It parses the XML with the browser's DOMParser, walks the element tree to infer a type model from child elements, attributes, and text, then emits Haskell data declarations with one record per distinct element shape.
Good to know
XML to Haskell turns a pasted XML document into ready-to-use Haskell record definitions, so you don't have to hand-write data declarations and field-by-field type signatures when you're modelling an external data feed, a config file, or a legacy API response. It's aimed at Haskell developers who need a fast starting point for parsing or deserialising XML and want the boilerplate scaffolding generated from a real sample rather than guessed at.
Reach for it when you have a concrete XML example in hand and want to skeleton out the corresponding domain types in seconds. The tool walks the element tree and emits one record per distinct element shape: the document's root element is renamed to Root, nested elements that have their own children or attributes become their own record types, and field names are camelCased and prefixed with the record name (so a title child of the root becomes rootTitle). Reserved Haskell words like type, data, or class get a trailing underscore so the output compiles.
To read the result, scan the field types: a bare type such as Int or Text is a required scalar, [Author] means that tag repeated under its parent and became a list, and Maybe X flags a field that was present in some siblings but missing in others. Attribute-derived fields and child-derived fields sit side by side in the same record. The status line reports how many types were generated, which is a quick sanity check on whether your structure was as nested as you expected.
One caveat: type inference is only as good as the sample you feed it. The tool decides between Int, Integer (for very long integers), Double, Bool, and Text by inspecting actual text values, so a numeric ID that appears with a leading zero or a blank value will fall back to Text. Paste a representative sample that exercises every optional and repeated element, then review the generated types and adjust by hand before wiring them into your parser.
Frequently asked questions
How are repeated and optional XML elements handled?
When a tag appears more than once under the same parent it becomes a list field (e.g. [Author]). When the tool sees the same element type across siblings and a field is missing in some, that field is wrapped in Maybe to mark it optional.
What types does it infer for element text and attributes?
Text content and attribute values are scanned: integers map to Int (or Integer if very long), decimals to Double, true/false to Bool, and everything else to Text. Elements with children or attributes become their own record type.
Is my data uploaded anywhere?
No — this tool runs entirely in your browser. Your input never leaves your device and it works offline once loaded.
Is it free?
Yes, completely free with no sign-up and no limits.
People also ask
Does this tool generate the Haskell code to actually parse the XML, or just the types?
It generates only the data type declarations (records, lists, and Maybe fields) plus the necessary imports and the OverloadedStrings pragma. You still need to supply or write the parsing/decoding logic, for example using a library like xml-conduit or hxt, to populate those records.
How does it decide between Int and Integer for numbers?
It treats a value as Int when it is a whole number up to 18 digits long, and switches to Integer for longer integer values that could overflow a machine Int. Values with a decimal point or exponent become Double, and anything non-numeric becomes Text.
Why is the root element renamed to Root in the output?
The tool standardizes the top-level element to a record called Root so the generated module always has a predictable entry-point type. Any internal field that referenced the original root type name is rewired to point at Root.
What happens if I paste invalid or malformed XML?
The tool uses the browser's DOMParser, which reports a parser error for unclosed tags or malformed markup. In that case the output is cleared and you get a message such as 'Invalid XML: check for unclosed tags' rather than partial code.
Does it keep XML attributes, and how are they represented?
Yes. Attributes on an element become fields in that element's record alongside fields derived from child elements, with their types inferred from the attribute values the same way text content is.
Can it handle an XML file whose root is just a single text value?
Yes. If the document's root element has no children and no attributes, the tool emits a newtype Root = Root Text deriving (Show, Eq) and notes that the root was a scalar value.
What typeclasses does the generated code derive?
Every generated record and newtype derives Show and Eq. If you need other instances such as Generic, Ord, or custom serialization, you add them yourself after pasting the output.
Is the generated Haskell guaranteed to compile as-is?
It is designed to be valid Haskell, with reserved keywords escaped and identifiers sanitized, but you should still review it. Inference from a limited sample can mismatch your real schema, and you may need the right imports for whichever XML parsing library you use.
Related tools