Fault-tolerant parsing
Reads XML and builds a usable document tree, even when the file is incomplete or has syntax errors.
This tool helps you work with XML in JavaScript and TypeScript projects. It can understand XML even when it has errors, and it gives editor-like features without needing Java or native software.
npm install xml-language-service
Install the package in an ESM-compatible Node.js project.
npm install xml-language-service
Create a service, parse XML, then call the feature you need.
import { getLanguageService } from "xml-language-service";
const service = getLanguageService();
const xml = `<catalog>
<book id="bk101">
<title>XML Developer Guide</title>
</book>
</catalog>`;
const document = service.parseXMLDocument("file:///catalog.xml", xml);
const completions = service.doComplete(document, { line: 1, character: 4 });
const hover = service.doHover(document, { line: 2, character: 6 });
const symbols = service.findDocumentSymbols(document);
const formatting = service.format(document, { tabSize: 2, insertSpaces: true });
console.log(completions.items);
console.log(hover);
console.log(symbols);
console.log(formatting);
await service.dispose();
The service returns plain TypeScript objects, so you can connect these features to your own editor, CLI, web app, language server, or test tool.
Reads XML and builds a usable document tree, even when the file is incomplete or has syntax errors.
Suggests element names, attributes, and closing tags while the user is typing.
Shows the attributes and children of the element under the cursor, or its schema documentation when a schema is associated.
Creates an outline of the XML structure so editors can show breadcrumbs, sidebars, and quick navigation.
Finds multi-line elements and comments that can be collapsed.
Returns text edits that re-indent the document and keep its line endings.
Renames matching opening and closing tags together so XML stays consistent.
Jumps between matching tags and finds other elements with the same tag name.
Reports syntax errors, including mismatched tags and undefined entities, and checks XML against XSD schemas with Apache Xerces-C++ compiled to WebAssembly.
Every feature works from the parsed document. A schema is optional and only adds information.
The fault-tolerant parser turns XML text into an XMLDocument: an element tree plus any syntax errors.
Completion, hover, symbols, folding, formatting, rename, definition and references all read that tree.
The file name and root xmlns are matched against your associations, then the built-in catalog. Completion and hover then use the schema's elements and attributes.
With a registered XSD, Xerces-C++ checks the raw text in one streaming pass. Without one, you get the parser's syntax errors.
Without a schema, validate() still checks that the document
is well-formed, including mismatched tags and undefined entities.
const document = service.parseXMLDocument("file:///a.xml", "<a><b></a>");
const diagnostics = await service.validate("file:///a.xml", document);
// includes: expected end of tag 'b' but found 'a'
To validate against an XSD, register it once, then validate any number of documents against it.
await service.registerSchema({
uri: "file:///catalog.xsd",
xsdText: catalogXsd,
});
const document = service.parseXMLDocument("file:///catalog.xml", xmlText);
const diagnostics = await service.validate("file:///catalog.xsd", document);
// [{ range, message, severity: "error" | "warning" | "info", source: "xsd" | "syntax" }]
If the XSD uses xs:include or xs:import, pass the
other files in imports. The keys must match the
schemaLocation values used in the XSD.
await service.registerSchema({
uri: "file:///root.xsd",
xsdText: rootXsd,
imports: { "types.xsd": typesXsd },
});
Pass the document's file name to doComplete() or
doHover() and the service looks up a schema for it. These
files are recognised out of the box:
| File | Schema |
|---|---|
pom.xml | Maven 4.0.0 |
web.xml | Java Servlet 3.1 |
persistence.xml | JPA 2.2 |
spring-*.xml | Spring Beans 4.3 |
const pom = service.parseXMLDocument("file:///project/pom.xml", text);
service.doComplete(pom, position, "pom.xml"); // modelVersion, parent, groupId, ...
Add your own associations. They take priority over the built-in ones, except for pom.xml.
service.addUserAssociation({
pattern: "**/*.config.xml",
xsdPath: "/absolute/path/to/config.xsd",
isBuiltIn: false,
});
resolveSchemaForDocumentAsync() downloads an XSD from an
http:// or https:// systemId and caches it in
.cache/xml-schemas/ under the current working directory.
Files it pulls in with xs:include or xs:import are not downloaded.
Use the printer helpers when you want to see what the parser produced.
console.log(service.printTreeAST(document));
// Document
// └── catalog
// └── book
console.log(service.printAST(document)); // elements and attributes
console.log(service.printAST(document, { format: "json" }));
console.log(service.printCST(document)); // every grammar rule and token
| Resource | URL |
|---|---|
| npm package | npmjs.com/package/xml-language-service |
| Repository | github.com/harshanacz/xml-language-service |
| Project using this | github.com/harshanacz/xml-review-bot-demo |
| XML language server based on this | github.com/harshanacz/xml-language-server |
| VS Code extension | github.com/harshanacz/xml-language-client |