Generate Content Using XPath

The content inserted by a Schematron quick fix operation can be static, such as text or tags specified in the quick fix operation, or can be generated dynamically depending on the current node content or depending on the other nodes from the document. To generate dynamic content, you can use XPath expressions in the value of the @select" attribute for the sqf:add, sqf:replace, or sqf:stringReplace operations.

By using XPath expressions, you can perform complex operations and generate content depending on the existence of some elements in the document or use content from other documents. You can benefit from more than 200 XPath built-in functions. There are functions for string values, numeric values, date and time comparison, node manipulation, and much more.

For example, perhaps you have a rule that checks if a section element has an @id attribute specified and reports any occurrences as a problem. Then, you can have a quick fix that adds an @id attribute on the current section element. The value of the attribute can be created by using the title section and removing any special characters or spaces from the title. In case the section does not have a title, you can generate a random value for the id.

Example 2. Use the title value as ID or generate a random ID

<sqf:fix id="addID">
  <sqf:description>
      <sqf:title>Add ID to the current section</sqf:title>
  </sqf:description>
  <sqf:add node-type="attribute" target="id"
      select="if (exists(title) and string-length(title) > 0)
      then substring(lower-case(replace(replace(normalize-space(string(title)),
                                                '\s', '_'),
      '[^a-zA-Z0-9_]', '')), 0, 50)
      else generate-id()"/>
</sqf:fix>

In case you want to add an @id attribute on all the section elements from the document, you can create a quick fix that matches all the selection elements that do not have an id attribute. To do this, you need to create a similar quick fix as the previous one and just add a match attribute on the sqf:add operation with the value: //section[not(@id)].

Example 3. Match all the section elements from the document and add an ID

<sqf:fix id="addID">
  <sqf:description>
      <sqf:title>Add ID to the all entries from document</sqf:title>
  </sqf:description>
  <sqf:add match="//section[not(@id)]" node-type="attribute" target="id"
      select="if (exists(title) and string-length(title) > 0)
      then substring(lower-case(replace(replace(normalize-space(string(title)),
                                                '\s', '_'),
      '[^a-zA-Z0-9_]', '')), 0, 50)
      else generate-id()"/>
</sqf:fix>

You can also use XPath functions such as document-uri(/) in case you want to add an id attribute with the same value as the file name.

Example 4. Get the current document file name and use it as ID

<sqf:fix id="addFileID">
  <sch:let
    name="reqId" 
    value="substring-before(tokenize(document-uri(/), '/')[last()], '.')"/>
  <sqf:description>
      <sqf:title>Set "<sch:value-of select="$reqId"/>" as ID</sqf:title>
  </sqf:description>
  <sqf:add node-type="attribute" target="id" select="$reqId"/>
</sqf:fix>