XML to Java

Generate Java classes from an XML document — attributes as fields, repeated elements as List fields, sometimes-absent primitives boxed so absence reads as null, optional jakarta.xml.bind (JAXB) annotations with the Java 11 classpath warning printed up front, and every 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 Java classes from an XML sample?

Java's XML world is class-first: JAXB, Jackson XML and their friends all want a model class per element before they will read a document, and writing those classes means transcribing somebody else's format — RSS feeds, SOAP responses, configuration dialects, export files — into fields with the right names, the right nullability and the right collection types. The sample document already contains every one of those facts: which elements repeat, which attributes come and go, which leaf values look numeric, which element is missing from half the records. This converter reads them off and writes the classes — plain public-field classes by default, or annotated for jakarta.xml.bind if you tick the box — and, because a sample is evidence rather than a contract, it reports every judgement 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. Optionally name the root class; leave it blank to take the root element's name. Decide on type inference — leave it on for samples you trust, off if the values are identifiers that merely look numeric.
  3. Tick the JAXB box if you want annotations; leave it off for plain classes you will wire to your own mapping.
  4. Press Generate Java classes. The root class comes first.
  5. Read the notes. Boxed fields, mixed content and skipped comments or processing instructions are all listed there — review them before you rely on the result.

The mapping rules

Elements. Each distinct element shape becomes a class named after the element, PascalCased; identical shapes share one class. A child that repeats anywhere becomes a List; a sometimes-absent child keeps its single type, boxed if it is a primitive. Attributes become fields in their element's class, marked as attributes. Text. A leaf element's text types its field — integer-looking text is int or long, decimal is double, true/false is boolean, mixed evidence falls back to String with a note — and text alongside children or attributes collects into a value field. Not data: comments, processing instructions and the DOCTYPE are skipped and counted in the notes; CDATA is read as text. Entities are decoded before inference, exactly as a real parser would hand them to you.

Worked example

Given this catalogue (the second book has a note the first lacks, and only the first has stock):

<catalog count="2">
  <book id="bk101" lang="en">
    <title>XML Guide</title>
    <price>44.95</price>
    <stock>12</stock>
  </book>
  <book id="bk102">
    <title>Java in Practice</title>
    <price>39.99</price>
    <note>Ships free</note>
  </book>
</catalog>

the converter emits:

import java.util.List;

public class Catalog {
    public int count; // attribute
    public List<Book> book;
}

public class Book {
    public String id; // attribute
    public String lang; // attribute
    public String title;
    public double price;
    public Integer stock;
    public String note;
}

stock is Integer, not int, because the second book proves it can be absent — and a plain int would make “missing” indistinguishable from “zero in stock”. That distinction is the sample's own evidence, written where Java needs it.

Frequently asked questions

How do attributes become fields?

An attribute becomes a field in the same class as its element, in document order before the child elements, and is marked with an // attribute comment (or @XmlAttribute in JAXB mode). If the attribute's name had to change to become a legal camelCase field — dashes, dots, a Java keyword — the comment carries the original XML name, because the comment (or the annotation) is what your deserialization code will match against.

Why is this field an Integer instead of an int?

Because the element is missing from at least one instance of its parent in your sample. A plain int cannot distinguish 'absent' from 'present with value 0' — both are 0 after unmarshalling — so the generator boxes it: Integer (and Long, Double, Boolean likewise) makes absence readable as null. Fields that repeat become List<T> instead and never box: an absent list is simply empty, which is already distinguishable. String fields never change — a missing String is null either way, so there is nothing to fix.

What does the JAXB option add, and what do I need for it?

It annotates the same classes for jakarta.xml.bind: @XmlRootElement on the root class, @XmlType on the others, @XmlElement / @XmlAttribute carrying the exact wire names (so field renames stop mattering to the unmarshaller), and @XmlValue for an element that has text plus attributes but no child elements. What you need: the jakarta.xml.bind API and a runtime implementation (such as GlassFish JAXB) on your classpath. Java 11 removed JAXB from the JDK, so those imports will not compile against a bare JDK — the generated file says so in a header comment before your compiler has to.

Can I turn off the int / double / boolean guessing?

Yes — untick 'Infer numeric and boolean types' and every text value is declared String. That is the conservative choice: a String field accepts any text, while an int field fails the whole unmarshal on the first non-numeric value. Inference is a convenience for samples you trust; remember it reads values, so a product code of 007 infers as the integer 7 unless you switch it off.

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

Into a String field named value, and the notes say it happened. Be clear about the loss involved: the text fragments are joined in document order, but the interleaving — which fragment came before which child — is gone, because no field-based model can hold it. If the interleaving is the information, that element wants a custom unmarshaller or a DOM walk, not a generated class; the generator's job is to tell you that plainly instead of hiding it.

How are namespaces like <dc:title> handled?

The name is used as written, prefix included, in the field's comment and in the JAXB annotation. That matches how these models are usually consumed — the prefixed name treated as a literal — but if your documents redefine prefixes per-document to mean the same namespace, one sample cannot tell the generator those are the same element. Normalise such a sample first, or merge the classes it produces by hand.

Related tools