XML to Go

Generate Go structs for encoding/xml from an XML document — attributes as fields, repeated elements as slices, sometimes-absent elements as pointers, and every inference and mixed-content judgement listed in the open. Free, no sign-up — runs locally in your browser.

Your work is auto-saved in this browser only, so a refresh won't lose it. Nothing is uploaded.

…or drop an .xml file anywhere on this panel.

Why generate Go structs from an XML sample?

Go's encoding/xml is a tag-driven library: the struct is the schema, and writing it by hand means transcribing every element and attribute of a format somebody else designed — RSS feeds, SOAP responses, SVG-ish exports, config dialects — into PascalCase fields with exactly the right xml: tags. That transcription is mechanical, and the sample document already contains every fact it needs: which elements repeat, which attributes come and go, which leaf values look numeric. This converter reads those facts off the sample and writes the structs, and — because a sample is evidence, not a contract — it reports every judgement call it made along the way.

How to use this tool

  1. Paste the XML, upload an .xml file, or drop one on the panel. The document must be well-formed; malformed input is reported with a line and column, not converted.
  2. Decide on type inference. Leave it on for samples you trust; turn it off if the values are identifiers that merely look numeric — a product code of 007 would become the integer 7.
  3. Press Generate Go structs. The root struct comes first.
  4. Read the notes. Pointer fields, sometimes-absent attributes, mixed content and always-empty elements are all listed there — review them before you rely on the result.
  5. Copy or download, add XMLName fields if you need root-name checking, and let go vet take it from there.

The mapping rules

Elements. A child element becomes a field tagged with its element name; one that repeats anywhere becomes a slice; one that is absent from some parents becomes a pointer so absence stays visible. Attributes become fields tagged name,attr in their element's struct. Text. A leaf element's text types the field — integer-looking text is int, decimal is float64, true/false is bool, anything mixed across instances falls back to string with a note. Text alongside child elements collects into a Text field tagged ,chardata. Shapes. Elements with identical structure share one struct type. Entities in text and attribute values are decoded before inference, exactly as a real parser would hand them to you.

Worked example

Given this catalogue fragment (note the second book has no price):

<catalog count="2">
  <book id="bk101">
    <title>Go in Practice</title>
    <price>39.95</price>
  </book>
  <book id="bk102">
    <title>XML Field Guide</title>
  </book>
</catalog>

the converter emits (with two notes: price became a pointer, and the count attribute was inferred numeric):

type Catalog struct {
    Count int `xml:"count,attr"`
    Book  []Book `xml:"book"`
}

type Book struct {
    Id    string   `xml:"id,attr"`
    Title string   `xml:"title"`
    Price *float64 `xml:"price"`
}

Every judgement in that output is the sample's own evidence: the slice is the second book, the pointer is the missing price, the attribute type is the digit 2.

Frequently asked questions

How do attributes become fields?

An attribute becomes a field in the same struct as its element, tagged `xml:"name,attr"`. Because encoding/xml matches attribute names exactly, the tag carries the attribute's name verbatim; the Go field name is its PascalCase form. An attribute that appears on only some instances of its element is still declared — the note tells you which ones, because encoding/xml has no omitempty for attributes: an absent attribute simply leaves the field zero-valued.

Why is this field a pointer (*T)?

The element appears under several parents in your sample and is missing from at least one of them. For a plain value field, encoding/xml cannot tell 'absent' apart from 'present but zero' — a missing <price> and a <price>0</price> both decode to 0. A pointer makes absent visible as nil. Repeated elements never become pointers: a missing slice is simply nil, which is already distinguishable from a present one.

Can I turn off the int / float64 / bool guessing?

Yes — untick 'Infer numeric and boolean types' and every text value is declared string. That is the conservative choice: encoding/xml will happily fill a string field from any text, while an int field fails the whole unmarshal on the first non-numeric value. Inference is a convenience for samples you trust; strings are the safe default for feeds you do not control.

The sample has text mixed with child elements. Where does the text go?

Into a Text field tagged `xml:",chardata"`, and the notes say it happened. Be aware of what chardata means: it concatenates every text chunk in the element into one string, so 'Hello <b>world</b> again' arrives as 'Hello world again' with the interruption point lost. If the interleaving matters to you, that element wants a custom UnmarshalXML, not a struct tag.

What about namespaces, like <dc:title>?

The element or attribute name is used as written, namespace prefix included, in both the tag and (PascalCased) the field name. encoding/xml matches prefixed names as literal strings when the tag says so, which is what the generated code does. If your documents redefine prefixes per-document, you will need to normalise the sample first — one sample cannot tell the generator that two prefixes mean the same namespace.

Does the generator ever drop anything from my XML?

No — but two things are worth knowing. Comments, processing instructions and a DOCTYPE outside the root element have no struct representation and are only counted in the notes. And text content of an element that also has element children beyond the chardata case: every distinct child element and attribute always becomes a field; nothing in the body of the document is silently skipped.

Related tools