Office Open XML (also informally known as OOXML) was initially standardized by the Ecma (as ECMA-376), and by the ISO and IEC (as ISO/IEC 29500) in later versions. OOXML consists of:
- a family of XML schemas, which define the XML vocabularies for word-processing, spreadsheet, and presentation documents; as well as
- the packaging of documents that conform to these schemas.
The XML schemas define sets of XML element and attribute names, which are organized in XML namespaces.
In .NET, the Open XML SDK provides tools for working with
Office Open XML, including XML namespace-related classes for
LINQ to XML. This repo provides
TypeScript classes (e.g., W
, X
, P
) for relevant XML namespaces and the XML names contained in such
namespaces. The namespace-related classes can be used with the @openxmldev/linq-to-xml
library, which
provides an implementation of LINQ to XML in TypeScript.
Run npm install @openxmldev/linq-to-ooxml
to install the library.
Run nx build linq-to-ooxml
to build the library.
Run nx test linq-to-ooxml
to execute the unit tests via Jest.
In short words, usage is identical to .NET. Have a look at the API documentation to see what is on offer.
Simply import the namespace-related classes from @openxmldev/linq-to-ooxml
. For example:
import { W, W14, WP, WPC } from '@openxmldev/linq-to-ooxml';
All class names are the uppercase versions (e.g., W
, W14
, WP
, WPC
) of the namespace prefixes
(e.g., w
, w14
, wp
, wpc
).
The following code snippet explains the anatomy of the namespace-related classes, using an excerpt
of the W
class. Firstly, with the exception of the NoNamespace
class, each class declares exactly
one property of type XNamespace
, using the namespace prefix as the name of such property. In this
case, the prefix is w
, so the property is called w
as well. Second, each class defines a getter
called namespaceDeclaration
, which returns an XAttribute
representing an XML namespace declaration
such as xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
. Again, the
NoNamespace
class is the exception because it does not represent a namespace but rather defines all
names that do not have a namespace.
class W {
public static readonly w: XNamespace = XNamespace.get(
'http://schemas.openxmlformats.org/wordprocessingml/2006/main'
);
public static get namespaceDeclaration(): XAttribute {
return new XAttribute(XNamespace.xmlns.getName('w'), W.w.namespaceName);
}
public static readonly document: XName = W.w.getName('document');
public static readonly body: XName = W.w.getName('body');
public static readonly p: XName = W.w.getName('p');
public static readonly r: XName = W.w.getName('r');
public static readonly t: XName = W.w.getName('t');
}
Third, each class defines properties for all names (e.g., w:document
, w:body
, w:p
) defined in
the namespace (or having no namespace in the case of the NoNamespace
class).
Using the names in conjunction with the classes provided by the @openxmldev/linq-to-xml
package,
you could create a super-simple "Hello World!" document as follows:
const document =
new XElement(W.document,
W.namespaceDeclaration,
new XElement(W.body,
new XElement(W.p,
new XElement(W.r,
new XElement(W.t, 'Hello World!')))));
The reserved keywords in C# and TypeScript are different, meaning that there are some minor differences in the naming of the static properties in C# vs. TypeScript.
In C#, the @
character is prepended where a property name is a reserved keyword. In case the
property name does not collide with a reserved keyword in TypeScript, the property name is used
as-is.
In TypeScript, collisions with reserved keywords or names are resolved by appending underscores
to property names. For example, name
and length
are reserved names in TypeScript, meaning
an underscore will be appended in those two cases.