Our very first XSLT assignment is an Identity Transformation, a kind of transformation we have to do frequently in our projects when we need to make specific changes to our encoding. For this exercise, we will write XSLT to make some repairs to an XML document from the Fall 2020 Elder Scrolls Morrowind project.
Begin with a directory of files posted on the digitProjectDesign-Hub. Using your Terminal (on Mac) or your Git Bash Shell (on Windows), update your local clone of the digitProjectDesign-Hub with git pull
. We will work with files in the Class Examples > XSLT > identityTransform
directory. Open your <oXygen/> XML Editor, and open the XML file named STARTguide2Balmore.xml
in that directory. (There is a Relax NG chema saved along with the files, and you will see that it is firing many validation errors on this document. That is because we made changes to the schema after this document was encoded, and we need to bring the XML document up to date with the new schema.
Our task is to deal with the validation errors in the document using an XSLT identity transformation. We do not want to change much of contents of this file, but we do want alter its tagging a little so the document will be valid according to its schema. That is a good occasion to write an Identity Transformation XSLT, converting our XML to XML that is meant to be (for the most part!) identical to the original.
Open a new XSLT file (version 3.0) and toggle the XSLT Debugger view so you can work on your XSLT transformation code in between the source XML and an output panel.(Or you may begin with the starter XSLT document provided in the same GitHub directory.)
Looking through the file, we can see the following tagging issues that are raising errors:
<who xmlid="Value with spaces">Name Of Someone</who>
<location xmlid="Value with spaces" visitable="yes">Name Of Place</who>
<NPC who="Value with spaces">Name of someone</who>
Here is what we want to do to repair the file.
@xmlid
attributes to @ref
, and we do not want to keep spaces in the attribute values.@xmlid
on the who
elements so that we keep only the first name (the substring before the first space) as the value, and put a #
in front of it. For example, we will change: <who xmlid="Codus Callonus">Codus Callonus</who>
into <who ref="#Codus">Codus Callonus</who>
@xmlid
on the location
elements so that separate the parts of the name using hyphens, and put a #
in front of it. For example, we will change: <location
xmlid = "Vivec City" visitable="yes">Vivec City</location>
into <location
xmlid = "#Vivec-City" visitable="yes">Vivec City</location>
@who
attributes on the NPC
elements. We don’t really want to have an attribute with the same name as an element in the document, and the values of @who
are overcomplicated, sometimes containing apostrophes and quotation marks, and the attribute is simply unhelpful. For our purposes with this exercise, we will just remove this attribute entirely without replacing it.You may already be calculating how to do these tasks with regular expression using Find and Replace, and while we know you could do that, our purpose is to make the changes using an XSLT transformation, and we hope you will learn some things about how XSLT works through this exercise.
version="3.0"
in our stylesheet template above. (You can see an old form here in the first template rule of our Identity transformation of Shakespeare’s sonnets, which you can download, save and open from here. That old first rule matches on all nodes, elements and attributes throughout the document and simply copies them. It’s perfectly fine to use that older template rule in place of the one we show you below, but we like the simplicity of this new form even better!).
This XSLT statement is the opposite of the xsl:template match we have been showing you in our XSLT tutorial. You basically say, if I do not write a template rule to match an element, attribute, or comment node, really of any part of the document that I do not mention in a template match rule, XSLT should simply make a copy of that element and output it. Try running this and look at your output: it will look exactly identical to the current XML document. Obviously we do not need to do this unless we want to make changes with template match rules! There is another way to copy, called "deep copy" in XSLT, but we do not want use it here. When you use "deep copy" in XSLT, you reproduce the full directory tree underneath a given element, so the understanding is that we would match on the root element only, and reproduce all the descendents of that one node just as they are. We like the "on-no-match-shallow-copy" approach because we need to step through the document tree just one step at a time. We only want to copy a node if we do not want to write a new template rule that will change it.
<NPC>
elements and simply remove all the @who
attributes from them in the output XML. Review our Introduction to XSLT to see how to write a template match on any particular element, and how to output an altered element in its place, and continuing to process its descendants using <xsl:apply-templates/>.<who>
elements that have @xmlid
attributes in the source document. In your template rule, reconstruct the <who>
element so that you rename the attribute, and write an AVT to return the value you want. Ideally, you want to do a little string surgeryon the original value: You want to output only the first part of it, the portion before the first space. Hint: There is a convenient XPath function for this:
tokenize()
. You could use the tokenize()
function to break the value into tokens using its spaces, and indicate with a position predicate ([1]
)that you just want to output the first token.<location>
elements that have @xmlid
attributes. Set a new attribute, @ref
, and again, set an ATV to pull its value, and again try a little string surgery: This time, try the
translate()
or replace()
function to convert the spaces in the original values into hyphens.<location>
elements, @visitable
, that we definitely need to preserve in the output! Be careful not to lose it. As you remodel the location
element, you can simply set the original attribute in place and use an ATV to set its value.Run-to-Endbutton. Eye-balling those results is not really enough because the Output window does not check for well-formedness or validation against a schema. Be sure to save those results, either by setting an output location in the appropriate place in the selection boxes, or by right-clicking on the output window and selecting
Save as. Always, always open the saved output file in <oXygen/> and check to make sure that it checks out as valid and well-formed. Your new output should address all of the schema validation errors and return a green square.
When you are finished, save your XSLT file and your XML output of the Elder Scrolls Morrowind file, following our usual homework file naming conventions, and upload these to the appropriate place in Canvas.