A well-formed document follows XML's own grammar, so any parser can read it. A valid document is well-formed and also obeys a schema for one vocabulary (Document Type Definitions to Schematron). A well-formed document must:
have exactly one root element, and close and properly nest every element (<bestseller/> is an empty one);
match names exactly, because XML is case-sensitive: <Price> and </price> differ;
quote every attribute value and never repeat an attribute on one element;
escape < and & in text, using the predefined <, >, &, ' and ", or a numeric reference such as é for "é".
In UTF-8, write "é" directly rather than as a reference, as older guides advise. XML parsers are draconian: after a fatal error they stop delivering data, where an HTML parser repairs its input. xmllint 3,427 --noout (parse, print only errors) checks a feed with three mistakes:
printf '<catalog>\n <book id=b7>\n <title>Tea & Toast</title>\n' > broken.xml
printf ' <Price>9.99</price>\n </book>\n' >> broken.xml
xmllint --noout broken.xml 2>&1 | grep 'parser error'broken.xml:2: parser error : AttValue: " or ' expected broken.xml:2: parser error : attributes construct error broken.xml:2: parser error : Couldn't find end of Start Tag book line 2 broken.xml:3: parser error : xmlParseEntityRef: no name broken.xml:4: parser error : Opening and ending tag mismatch: Price line 4 and price broken.xml:5: parser error : Opening and ending tag mismatch: catalog line 1 and book
libxml2 3,427 keeps scanning but never delivers the document. The first three lines are one mistake (the unquoted b7) and the last is its knock-on effect: fix the first error, then parse again. Validity needs a schema, which xmllint checks with --valid (DTD), --schema (XSD) or --relaxng. In a pipeline, reject files that are not well-formed and quarantine those that fail their schema, with the report, for a person to judge.
VS Code 550 's Red Hat XML extension checks both as you type; oXygen and XMLSpy 106,997 are the commercial editors.