XSLT 2.0, the latest specification released by the W3C, is a language for transforming XML documents. It includes numerous new features, with some specifically designed to address shortcomings in XSLT 1.0. In this collection of articles, you'll get a high level overview and an in-depth look at XSLT 2.0 from the point of view of an XSLT 1.0 user who wants to fix old problems, learn new techniques, and discover what to look out for. Examples derived from common applications and practical suggestions are provided if you wish to upgrade. To help you begin to use XSLT 2.0, migration techniques will be provided. Check the library for additional articles in this Planning to upgrade series.
XSLT is getting bigger and better
XSLT is widely adopted and applied to a broader range of problems than the XSL Working Group (WG) anticipated. If you write XSLT stylesheets, you probably know certain coding patterns to obtain results that go beyond the plain meaning of the code. What springs to mind when you see generate-id()? node-set()? substring-before()? disable-output-escaping? XSLT 2.0 will soon be a final recommendation, and you can use it to make your code more readable. The new XSLT features let you use terms that are closer in meaning to your intent.
XSLT 1.0 was mainly defined in two W3C documents: XSLT and XPath. The 2.0 version is designed to align with XQuery, and the XSLT family now includes six specifications in its core: XSLT, XPath, Functions and Operators (F&O), Data Model (XDM), Formal Semantics, and Serialization. (See Resources for links to the W3C documents.) Like 1.0, it is built upon several other foundation specs (XML, namespaces, and so on.), and it draws XML Schema into its orbit. These were preceded by a requirements document that stated objectives for the new features and justified the work on a 2.0 specification.
This article is based on the Candidate Recommendation from June, 2006 and describes the enhancements in XSLT 2.0 that are most likely to convince you to upgrade to the new version. While the W3C might change some details between Candidate Recommendation and the final Recommendation, the essence of this article will still apply.
One of the earliest special techniques to appear in XSLT 1.0 was a means to process a set of elements, each of which had an attribute (or descendant) that indicated the group to which it belonged. Grouping of elements was not addressed in the 1.0 specs. Early 1.0 stylesheet writers refined a technique that is not plainly readable in the code, but often recognized when a for-each loop has the pattern
<xsl:if test="generate-id(.)=generate-id(set[@grp=$thisvalue][1])">... once-per-group code ...</xsl:if>
where the comparison of generate-id() values is an attempt to discern whether the current node is the first encounter with $thisvalue in the @grp attribute. This is-it-the-first-node method can be especially inefficient when you must filter the larger set (the population) once for each different value ($thisvalue in this example) used as a key.
Grouping in 2.0 is implemented by the new xsl:for-each-group instruction and two supporting functions, current-group() and current-grouping-key(). These work over a set of similar nodes, much like xsl:for-each and the current() function. And like for-each, for-each-group can take xsl:sort as a child element to control the order in which the groups are processed. If you are comfortable with xsl:for-each, you are likely to find xsl:for-each-group easy and natural to use. Grouping should improve performance because the processor will be able to optimize the means of detecting group boundaries. Vast amounts are likely to be written about grouping and the techniques to achieve the desired results, so this article just introduces a few simpler uses here, such as grouping a population of elements, which is the situation in the vast majority of cases.
You can divide a population into groups in four ways. Three ways take note of the order of appearance of the nodes being grouped, while the fourth way groups according to a keying value in a manner that is familiar to SQL users. Each time you use the xsl:for-each-group instruction, you will designate one of the four methods:
In the group-by method, you define an expression that tells where to obtain the key value for each member of the population. (This is like @use in xsl:key or @select in xsl:sort.) In a typical situation, a population of many elements is organized into groups based on an attribute that each element is known to have. Suppose you have financial data for many branch offices of your organization, and each branch is assigned a geographical region. In XML, the data might look like this:
Listing 1. Data that is amenable to grouping
<branch id="B723" region="Northeast">...data...</branch> <branch id="B201" region="North">...data...</branch> <branch id="B558" region="Northeast">...data...</branch> <branch id="B064" region="West">...data...</branch> etc. |
To gather the <branch> elements into regions, use this instruction:
<xsl:for-each-group select="branch" group-by="@region">... once-per-region code...</xsl:for-each-group>.
Within the body of the loop, you have current-grouping-key() to know which @region value is the current one, and you can use
<xsl:for-each select="current-group()">...once-per-branch code...</xsl:for-each>
to iterate over the current group, all the branches in one region. Note that it is easy to get a list of all the grouping-key values (region names in this example) without setting up grouping; just use the new distinct-values() function. For more information on the distinct-values function, read the article "XML for Data: What's new in XPath 2.0?" (see Resources).
The group-by method is not concerned with the sequence of nodes. It allows multiple keys, which raises the possibility of duplicate nodes in a group. The rule is that any given group must have only one occurrence of each member node. However, the same node can be a member of several groups.
The group-ending-with method is designed to step through a population of elements placed in a particular order and having flag nodes every so often to indicate the end of a group. In a numerical application, this is like encountering <subtotal>, <total>, and <grand-total> flags in a list. In xsl:for-each-group, @group-ending-with uses XSLT pattern syntax to specify how the flag node is recognized. The XSLT 1.0 equivalent would be some painful combination of position(), recursive processing, the preceding-sibling axis, and possibly two-phase processing.
The group-starting-with method is very similar to group-ending-with, but the flag node is at the front of the group. You might use this form of grouping when you process the XML versions of the Shakespeare plays. (These have been available on the Web for many years.) There are <speech> and <line> elements that occur in sequence. The character speaking is identified in a separate element that precedes the lines, and <StageDir> elements occur wherever needed in a scene, but in their proper place in the sequence. For some uses, you might want the elements that are siblings to have been nested, or vice versa, but grouping can overcome those problems. One possibility is to flag the <StageDir> elements to group all the other elements that occur while a particular staging is in effect.
The fourth grouping method is group-adjacent. Like group-by, it uses node values rather than patterns to demarcate groups, but it is aware of the sequence of elements in the population. Conceptually, it says to monitor a particular node (attribute or descendant) that each element will have and form a group when the value of that node changes from the last value. This can be used to form groups of fixed size, using the following soon-to-be-famous technique:
<xsl:for-each-group select="item" group-adjacent="ceiling(position() div 4)"> |
This puts <item> elements in groups of four.
Incidentally, XSLT 2.0 provides some operators that make detection of node order or node equality more convenient. The use of generate-id() shown at the start of this section is really about discerning whether two paths point to the same node. In 2.0, an operator for this purpose is called is. Similarly, the >> and << operators allow checking of whether one node is later or earlier in document order than the other. This can simplify any look-ahead needs not already addressed by xsl:for-each-group.
There are many other tips and techniques for grouping. For now, this section should you give a general idea of the potential.
One great source of frustration in XSLT 1.0 comes from the limitations on Result Tree Fragments (RTFs). When a variable contained a tree, you could copy it into the result set or treat it as a string, but you couldn't navigate it as a tree with just the standard XSLT/XPath tools. Perhaps like many other people, you read and re-read the paragraph in part 11.1 that said, "A result tree fragment is treated equivalently to a node-set that contains just a single root node. However, the operations permitted on a result tree fragment are a subset of those permitted on a node-set. An operation is permitted on a result tree fragment only if that operation would be permitted on a string...." As you read it, you wondered why it was not enough of a tree to be navigated by XPath expressions.
Most providers of an XSLT processor added an extension function, usually called node-set(), which allows the RTF to become a navigable and filterable tree. Recall that in 1.0, xsl:variable could take @select and its value would be either a node-set or single atomic value, or it could create an RTF by having a content constructor as child elements. That latter form creates an Implicit Document Node (IDN) in 2.0, which is one form of the new temporary tree structure. If a 1.0 stylesheet created RTF variables, the same stylesheet versioned for 2.0 would create IDNs. You still need a content constructor (renamed sequence constructor for 2.0) in xsl:variable if you need programmatic instructions like xsl:choose or xsl:call-template, which forces you to create an RTF/IDN tree. You can use xsl:copy-of to copy the whole IDN tree to the output, as before, but you can also use the variable in a path expression without a node-set conversion.
The IDN, or a sub-tree of it, can be in the select expression of xsl:apply-templates, allowing the tree to be processed in the same manner as an XML input document. If done on a large scale, this is a form of two-phase transformation without having separate processor runs for phase 1 and phase 2. Two or more phases are useful when you need to construct an XML document with extensive cross-references. Avoidance of the node-set function eliminates bugs where two copies of the tree, one RTF and one node-set, get confused. For large temporary trees, this also saves memory. If you set up ID-type attributes or suitable key values when you constructed the IDN, you can use the id() and key() functions for indexed access, and the 2.0 versions of those functions even have a convenient extra argument to limit indexed retrieval to a designated tree.
The IDN variable reference can be followed by steps of a path (for example, /child) and a filter (for example, [@foo="yes"]), which allows you to select a set of nodes for a xsl:for-each loop, or address values to be used as you would in any other select expression. The same abilities apply to the test expression in xsl:if and xsl:when.
Note that xsl:variable has options to control data typing. Among other things, this allows xsl:variable with sequence constructor content instead of @select to not create a full-blown IDN but create standalone attribute nodes, comments, and processing instructions. It can create standalone elements that have content, which, unlike IDNs, do not have a document node at the top of the tree. It can also create sequences of atomic values, which might be confused with node-sets. The following example shows how to use @as to suppress the creation of a document node and any elements, and instead create a sequence.
Listing 2. Variable conditioned by @as to be a sequence of xs:double values with no document or element nodes
<xsl:variable name="values" as="xs:double*">
<xsl:sequence select="(1,3,5)"/>
<xsl:if test="@m='large'">
<xsl:sequence select="(2,4,10)"/>
</xsl:if>
</xsl:variable> |
The variable $values is neither a node-set nor an IDN; it is just a sequence of atomic double-precision numbers. (Note: Throughout this article, the namespace prefix "xs" refers to the XML Schema namespace for datatypes.)
The new xsl:function instruction introduced in XSLT 2.0 lets stylesheet writers create their own function in a stylesheet using pure XSLT syntax and semantics. This is, of course, only necessary if you can't find the function you need among the 128 functions and 68 operators available from the F&O and XSLT 2.0 specifications (see Resources). See the subsequent section on character maps for an example of xsl:function.
Without the ability to create user-defined functions easily, XSLT 1.0 stylesheet writers often tried to accomplish the task by either calling a named template, defining their own extension function in another language, or hope the XSLT 1.0 processor supports pseudo-standard extension libraries like EXSLT (see Resources for a link to the EXSLT site). Some functions in the F&O library overlap with functions defined in EXSLT, while each library provides functions not supported by the other. EXSLT also defines an extension instruction func:function with identical syntax to xsl:function. (If you already use func:function, you should switch over when you adopt XSLT 2.0.) The drawback with using extensions from EXSLT is that some implementations only provide selective support, and there is no guarantee that those implementations will continue to support these extensions in the future.
To write your own extension function in another language is often complex and time consuming. Your implementation of the extension function might be confined to work in only one processor and is always limited by the mechanisms and data structures exposed by that processor. In XSLT 1.0, writing a named template to achieve the desired functionality is the safest alternative with no implementation-specific restrictions. However, to call a named template requires more XSLT instructions, while to call a function is more compact and can easily be embedded in an XPath expression. Another restriction is the limitation involved in result tree fragments when calling a named template from the content of xsl:variable. (See the earlier section, Implicit document nodes).
If you have a 1.0 stylesheet but it no longer runs with a 2.0 processor because it requires the implementation of an extension function, first look in the F&O and XSLT 2.0 specifications to see if the work is already done for you. If you don't find what you need, then learn to write your own function in the stylesheet. For some but not all EXSLT functions, EXSLT provides a near equivalent named template, which you can use as a guide to help you write the function in XSLT syntax. (Remember that EXSLT is meant for XSLT 1.0, so using new 2.0 syntax would further simplify your function.) If you find that an extension function can do a better job than a stylesheet function, then you can define the stylesheet function to be the second choice when the extension function is not available.
The XSLT 2.0 family of standards offers support for date/time data, including a set of functions and operators that can perform time-oriented calculations. No longer do you need to handle dates and times as strings and pull apart the strings to get individual numbers or month names. The data types are taken from the XML Schema Part 2 specification (see Resources), which gives you both points in time and durations. Values of these types can come from the input document of a transformation, if schema awareness is in effect (see Schema awareness further ahead), or as the return value of a function. Several XSLT 2.0 instructions have mechanisms such as the new @as attribute (see the @as example under Implicit document nodes) to assert the data type.
When you want to create an atomic value of one of the time-oriented data types, use a type-constructor function and provide the string representation as an argument. For example, xs:time('12:34:56')
takes the string representing the time shown and returns a value that has a type annotation to mark it as a true time-of-day value. The constructor functions in this category are the same as the names of the built-in types in XML Schema: xs:date, xs:time, xs:dateTime, xs:duration, xs:gYear, xs:gMonth, xs:gDay, xs:gYearMonth, xs:gMonthDay, with the addition of xs:yearMonthDuration and xs:dayTimeDuration.
Once you have 12:34:56 as a true time-of-day value, you can add a duration value to it to yield a later time. Many new functions and operators are provided that allow all suitable arithmetic operations between points in time and durations. You can multiply durations by a scalar number to get a longer or shorter duration and can divide by a scalar number or another duration. One dateTime subtracted from another yields a duration as the difference. You can also compare two values of the same type. For example, a dayTimeDuration of PT90S (90 seconds) compares equal to the normalized value PT1M30S (1 minute 30 seconds). For some types, inequality relationships are defined. Greater-than for duration means a longer duration; greater-than for date means a later date.
Also, a set of component extraction functions allows one operation, with a readable name, to get one field out of a date, time, or duration. For example, compare
minutes-from-dateTime(xs:dateTime('2005-07-01T14:06:32'))
|
returning the integer 6 to the old way, where a tangle of substring-before() and substring-after() functions and, probably number(), would be used to extricate the minutes. In the new way, the name of the function explains your intention.
If you have 1.0 stylesheets that manipulate time-oriented values extensively, you probably have some operations that are performed by calling specialized named templates. This is a disadvantage if the return value of the template needs to feed into an XPath expression because you need to wrap xsl:variable around the call to the template, and that variable is an RTF (see Implicit document node). In general, xsl:function (see User-defined functions) is the 2.0 solution, but with time-oriented values, you might find that the 24 new functions and 44 new operators, plus the constructor functions, cover your needs. By comparison, the EXSLT package (see Resources), which some XSLT 1.0 processors offer, has only 28 time-oriented functions, including the ones that do formatting.
By popular demand, the new function set also has functions to return the current time, date, or dateTime, so a stylesheet can time-stamp its output.
All of the above refers to treatment of the values as truly being of their respective types. The functions are shared between XSLT and XQuery. Because XSLT still has a mission to produce presentable information, XSLT adds formatting functions that take a date, time, or dateTime typed value and produce a string with the decorations you might need, such as month names spelled out. The XSLT 2.0 specification allows each implementation to make choices about languages, numerals, and similar aspects. If this is important to you, start composing your want list now so that you can check features on the 2.0 processors that will be available when the spec is finalized.
Schema-awareness is an optional feature in XSLT 2.0. If you find a processor that supports it, you might find it worthwhile to use it. The schema-aware feature is mainly used for error checking. It validates your input and output against an XML schema, and it allows you to refer to source nodes in the stylesheet based on their Schema type. For 1.0 stylesheet writers, this is a feature designed to help you detect errors early in your transformation before the frustration of debugging hits you. The following mechanisms are available to enable you to take advantage of this feature:
Table 1. Instruction attributes and XPath components that can be schema-aware
| Syntactic item | Usage |
|---|---|
@as attribute | Use this attribute on xsl:variable, xsl:param, xsl:function, or xsl:template to assert that the generated result tree or sequence is an instance of a desired Schema type. |
instance of | Use this operator on a sequence or a single atomic value or node to verify that it is an instance of a desired Schema type. |
@validation and @type attributes [when on an Literal Result Element (LRE), @xsl:validation and @xsl:type] | Use these attributes to validate nodes based on a Schema type definition or against a top-level element or attribute definition. |
cast as and constructor functions | Cast an atomic type to a Schema type. |
castable as | Operator that checks whether a cast will succeed. |
node-test element(*,type) and attribute(*,type) | Return only element or attribute nodes with the desired Schema type annotation. |
node-test schema-element(elemDecl) and schema-attribute(attribDecl) | Return element or attribute nodes that match a Schema element or attribute definition. |
Output (Serialization) enhancements
One 2.0 stylesheet can produce multiple result trees to be serialized in multiple destinations. The developerWorks article, "Create multiple files in XSLT 2.0" (see Resources) can give you a general idea of how to use the new xsl:result-document instruction.
The disable-output-escaping (d-o-e) attribute is often incorrectly used by 1.0 stylesheet writers without a full understanding of the distinction between markup and text. It is also architecturally flawed because it requires the serializer to be aware of an additional field per node beyond the information exposed by the XPath data model and Serialization parameters. Thus, the boundary between transformation and serialization is muddled, so the XSL WG deprecated this attribute. In its place, they introduced character maps, which are mappings of characters during serialization where all occurrences of a specific character are mapped, even if it would result in XML that is not well-formed, to another character requested by the use-character-maps Serialization parameter. The following stylesheet illustrates how to use character maps by replacing @disable-output-escaping with the my:d-o-e function. You can also use this function as an example to achieve character mappings only on selective nodes in the result tree. This example relies on mapping infrequently used characters such as a left angled quote («, often coded as «) and assumes that such characters do not appear in the resulting sequence otherwise. Use even more rare characters if necessary to assure that only the desired characters are mapped.
Listing 3. The better way to get special XML characters into your output
<xsl:output use-character-maps="my:charMap" />
<xsl:character-map name="my:charMap">
<xsl:output-character character="«" string="<" />
<xsl:output-character character="»" string=">" />
<xsl:output-character character="§" string="&" />
<xsl:output-character character="¤" string="'" />
<xsl:output-character character="¦" string=""" />
</xsl:character-map>
<xsl:function name="my:d-o-e" as="xs:string">
<xsl:param name="str" as="xs:string" />
<xsl:sequence select="translate($str, '<>&'"',
'«»§¤¦')" />
</xsl:function>
<xsl:template match="/doc">
Replace
<xsl:value-of select="." />
With
<xsl:value-of select="my:d-o-e (.)"/>
</xsl:template> |
Use the following character map to prevent escaping all ampersand (&), less than (<), greater than (>), apostrophe ('), and double quote (") characters. Though it appears that it simply maps each character to itself, it actually avoids the escaping (according to XML or HTML rules) phase in Serialization because this phase cannot be applied to characters that are mapped.
Listing 4. Another way to preserve special XML characters in unescaped form
<xsl:character-map name="my:charMap">
<xsl:output-character character="<" string="<" />
<xsl:output-character character=">" string=">" />
<xsl:output-character character="&" string="&" />
<xsl:output-character character="'" string="'" />
<xsl:output-character character=""" string=""" />
</xsl:character-map> |
Unicode normalization was requested by XSLT users and, in response, two options to perform normalization are provided in XSLT 2.0. To achieve selective normalization, use the F&O function normalize-unicode(). Alternatively, the Serialization parameter normalization-form affects the entire final result tree. If character mapping is used, then first consider whether you want to apply normalization to the character being mapped, or apply normalization after the character is mapped. Use normalize-unicode() for the first scenario, and apply parameter normalization-form for the latter.
Three functions for URI escaping
The F&O specification defines three functions, encode-for-uri(), iri-to-uri(), and escape-html-uri(), to perform percent-encoding on a string that results in a URI or part of a URI.
In URIs, you can escape characters that would have syntactic effect by replacing the actual character with a percent sign followed by two hexadecimal digits, such as %20 for space.
EXSLT defines str:encode-uri(), which also performs percent-encoding, but for five fewer reserved characters compared to encode-for-uri(). Depending on your needs, the three functions in F&O should suffice. To control escaping URI attributes in HTML output, use the escape-uri-attributes Serialization parameter. This parameter was not available in XSLT 1.0, and thus, URI escaping was always done.
Following is a list of printable characters from the US-ASCII coded character set, specifically the octets ranging from 32 to 126 (decimal), that the functions will percent-encode:
fn:iri-to-uri()encodes " < > \ ^ ` { | } space- EXSLT's
str:encode-uri()encodes " # $ % & + , / : ; < = > ? @ [ \ ] ^ ` { | } space fn:encode-for-uri()encodes ! " # $ % & ' ( ) * + , / : ; < = > ? @ [ \ ] ^ ` { | } spacefn:escape-html-uri()does not encode any of these characters
The new xsl:namespace instruction is an obvious and simple solution to create a namespace node, addressing the lack of such a mechanism in XSLT 1.0. In XSLT 1.0, naïve users might have tried to create one by calling xsl:attribute with "xmlns:prefix" as its name. Namespace nodes could actually only be created indirectly, either through copying element or namespace nodes from the source, copying namespaces from the stylesheet if an LRE is instantiated, or by direct creation of an element or attribute whose name is in a namespace. In other words, 1.0 forced you to either have the namespace node already created or to create some other unwanted node to force the namespace node as a by-product.
XML 1.1 (prefix undeclaration and unicode normalization)
You can support XML 1.1 with conformant XSLT 1.0 processors as of the last errata publication (see Resources). XML 1.1 is optional for both XSLT 1.0 and 2.0. However, if it is supported, you should still be aware that the output attributes normalization-form and undeclare-prefixes are available for XSLT 2.0 but not for XSLT 1.0.
XSLT 2.0 added an additional output method, XHTML, which must be supported by all conforming processors with Serialization. Serializing XHTML is still possible in XSLT 1.0 by outputting in XML and setting the doctype-system and doctype-public attributes on xsl:output. This ensures that a <!DOCTYPE> declaration is serialized. However, this might not be enough for some agents designed to handle HTML because of subtle differences listed in Appendix C of the XHTML specification (see Resources).
Separator attribute for xsl:value-of
Possibly the favorite XSLT 1.0 instruction is xsl:value-of, which can be used to push a value onto the output as a string. Many stylesheet writers use it as a debugging tool, but they find that they also need to use count(expr) when they aren't sure how many nodes expr has. In 2.0, xsl:value-of does not put out just the first member of a set, but actually iterates over the whole sequence, and you can even control the separator string. (If you depended on xsl:value-of taking just the first member, you can set version="1.0" on the instruction if the backwards compatibility feature is supported by your 2.0 processor.) The simple instruction
<xsl:value-of select="/doc/*" separator="', '"/>
replaces all the code below:
Listing 5. Typical 1.0 code to format a list of values, which can be replaced by one instruction in 2.0
<xsl:template match="/doc">
<xsl:call-template name="valueOfWithSeparator">
<xsl:with-param name="nodes" select="*" />
<xsl:with-param name="separator" select="', '" />
</xsl:call-template>
</xsl:template>
<xsl:template name="valueOfWithSeparator">
<xsl:param name="nodes" />
<xsl:param name="separator" select="' '" />
<xsl:for-each select="$nodes">
<xsl:if test="not(position() = 1)">
<xsl:value-of select="$separator" />
</xsl:if>
<xsl:value-of select="." />
</xsl:for-each>
</xsl:template> |
To control the byte order mark (BOM) in the stylesheet, you can now use the byte-order-mark Serialization parameter. Note that implementers are permitted to override this value. In XSLT 1.0, implementations can put a BOM for UTF-8 documents, and would likely do so for UTF-16 and UTF-32. In XSLT 1.0, either the implementer has to support an extension attribute or the user needs to apply an external (post transformation) process to ensure a BOM is added, or to strip it.
This article described the major features in XSLT 2.0 most frequently requested, leaving XPath and the function library for later. If you haven't yet seen the feature that convinces you to upgrade, keep on looking.
Learn
-
The W3C site: Visit this great source of information on standards such as:
- XSLT 1.0
- XSLT 2.0
- XSLT 2.0 Requirements
- XPath 1.0
- XPath 2.0
- Functions and Operators (F&O)
- Data Model (XDM)
- Formal Semantics
- Serialization
- XQuery
- XML Schema
- XML 1.1
- XHTML
- Namespaces in XML
- "XML for Data: What's new in XPath 2.0?" (Kevin Williams, developerWorks, September 2002): Dig into a longer description of the
distinct-values()function and its usage, as well as examples ofcurrentDateTime()and a constructor function for a Schema data type. - The EXSLT site: Review a set of extension functions for XSLT 1.0. Click on the dates and times module to see the time-oriented capabilities.
- "Create multiple files in XSLT 2.0" (Jack Herrington, developerWorks, March 2005): Explore examples of
xsl:result-documentin action in this developerWorks tip. - "XSL Transformations (XSLT) Version 1.0 Specification Errata" : Check out the accumulation of corrections and clarifications that have been approved by the XSL Working Group.
- IBM XML certification: Find out how you can become an IBM-Certified Developer in XML and related technologies.
- XML technical library: See the developerWorks XML Zone for a wide range of technical articles and tips, tutorials, standards, and IBM Redbooks.
- developerWorks technical events and webcasts: Stay current with technology in these sessions.
Get products and technologies
- IBM trial software: Build your next development project with trial software available for download directly from developerWorks.
Discuss
- XML zone discussion forums: Participate in any of several XML-centered forums.
- developerWorks blogs: Get involved in the developerWorks community.
David Marston has worked with XML technologies since late 1998, particularly on standards conformance. Over his 25+ years in the computing business, he has been involved with all aspects of software development. He is a graduate of Dartmouth College and a member of the ACM. He is on the Next-Generation Web team at IBM Research. You can contact him at David_Marston@us.ibm.com.