Paste F# on the left and click "Convert" — we will turn it into XMLPaste F# code

What this tool does

If you write F# and work anywhere near .NET XML config, WCF contracts, or a SOAP endpoint, you already know the pain: your record types are clean and expressive, but hand-rolling the XML that matches them is a slog. Paste the F# here and get back well-formed XML in one pass — a single record, a populated let binding, or a whole module with nested records.

The converter knows the F# quirks you actually care about. An option<string> holding Some "x" becomes a normal element; None becomes an empty element rather than being dropped, so your XML shape stays predictable. decimal literals (249.99m) drop the suffix. list, seq, and arrays become container elements with one child per item — matching what System.Xml.Serialization emits under the hood when you round-trip through .NET.

Discriminated unions are handled sensibly — the case name lands as the element tag and the payload becomes child elements. Nested records expand inline, tuple fields come through as sibling elements, and Map<K,V> becomes <Entry><Key/><Value/></Entry> pairs. For attribute-driven customization ([<XmlElement>], [<XmlAttribute>]), see the F# docs on .NET interop — the converter honours those when present.

How to use it

Three steps. Works the same way whether you paste a one-line record or a whole module.

1

Paste your F# (or try the sample)

Drop the F# into the left editor as-is. A record type, a populated let binding, multiple records, or a discriminated union — all fine. Click Load Sample for a realistic example first.

You do not need to strip open statements or clean up the F# syntax. Leave the code the way it looks in Rider or VS Code. Just paste.

2

Hit Convert

Click the green Convert button. The tool parses the records, walks the populated value, and builds the XML in one pass. A short loading indicator runs while it works.

3

Copy the XML

The right panel fills with indented, well-formed XML that any standards-compliant parser will accept. Copy it straight into your app.config, a SOAP fixture, an XmlSerializer round-trip test, or your docs.

When this actually comes in handy

.NET XML config files

An F# record that models an app.config / web.config section turns into a ready-to-edit XML template — no hand-writing angle brackets.

WCF and SOAP fixtures

You have an F# record that mirrors a WCF data contract. Paste it, get the SOAP-shaped body, drop it into SoapUI or Postman.

Fable interop with legacy XML endpoints

Using Fable to talk to an older XML API? Paste the shared F# record and get the XML body your backend expects, with option fields handled correctly.

Test data for XmlSerializer

Generate XML seed data that round-trips through <code>XmlSerializer&lt;T&gt;</code> without surprises — helpful for integration tests, mock servers, and regression suites.

Common questions

Can I paste multiple records at once?

Yes — paste a whole module. Each record type comes through with nested records expanded and any and-chained type definitions handled. Discriminated union cases keep the case name as the element tag.

How does it handle option, None, and defaults?

An option<string> with Some "x" becomes a normal element containing the text. None becomes an empty element so the shape stays stable — you will not lose fields between records that are populated and records that are not. voption behaves the same way.

What about decimal, DateTime, Guid, and the tricky types?

Decimals (249.99m) drop the suffix and become plain numeric text. DateTime, DateTimeOffset, and TimeSpan become ISO-8601 strings. Guid stays in the canonical 8-4-4-4-12 hex format. byte[] becomes base64 text — matching XmlSerializer behaviour.

Do discriminated unions work?

Yes. A DU like type Status = Active | Suspended of reason: string emits <Active/> for the nullary case and <Suspended><reason>...</reason></Suspended> for the payload case. The case name lands as the element tag — which is usually what you want for XML that matches a schema.

Is my code stored?

Your code is sent to the backend for conversion and is not persisted — we do not log the payload. If the F# is sensitive (internal contracts, API keys in literals), look it over before pasting.

What if the F# uses computation expressions or active patterns?

Those do not produce XML fields — they are control-flow constructs, not data. We look at the record type definitions and the populated instance, so an async { ... } or a banana-clip pattern match is ignored. Fix any obvious syntax errors first so the parser has something clean to chew on.

Other tools you may need

F# to XML is one piece of the puzzle. These tools pair well with it: