Maintained by: David J. Birnbaum (djbpitt@gmail.com)
Last modified:
2021-12-27T22:03:53+0000
This document includes brief examples of issues that frequently arise in Relax NG homework. Examples of bad code have a pink background.
The schema must be associated correctly with the XML. Details about how to get <oXygen/> to insert the schema association instruction can be found in http://dh.obdurodon.org/relaxng.xhtml.
<?xml version="1.0" encoding="UTF-8"?> <?xml-model href="ding_RNGex2.rnc" type="application/relax-ng-compact-syntax"?> <article> <!-- stuff goes here--> </article>
White space is up to you. We recommend letting <oXygen/> pretty-print your Relax NG, which will ensure that your spacing is consistent. Example:
sentence = element sentence { mixed { ( adj | meta | place )* } }
Patterns are examples of good practice. Anti-patterns are examples of bad practice that are nonetheless common because they don’t look like bad practice on first acquaintance. Of course there’s a Wikipedia page for the term: https://en.wikipedia.org/wiki/Anti-pattern!
This structure allows any of the specified elements to appear zero or more times in any combination and in any order, with plain text optionally before, between, or after the element.
body = element body { mixed { ( country | location | continent )* } }
The following alternatives are correct Relax NG. We prefer the one above because
the use of the mixed
keyword makes it easy to see that mixed
content is involved, but we should also accept:
body = element body { ( country | location | continent | text )* }
body = element body { mixed { country* & location* & continent* } }
The bad example below works, but the question marks serve no purpose, since the corrected version after it has the same meaning and is easier to read and to debug.
p = element p {mixed { (quote? | loc? | q? | pr? | slavojism?)* }
p = element p {mixed { (quote | loc | q | pr | slavojism)* }
Attributes are not repeatable on their element (this is a well-formedness
constraint), which means that they should never have *
or
+
repetition indicators in a schema. Relax NG will—sloppily—let
you write those repetition indicators, but that won’t make the attributes
repeatable, since you cannot override a well-formedness constraint. Because the
attributes are not repeatable, misrepresenting the structure as if they were is
bad practice.
The bad example below might represent an attempt to allow for simultaneous speech by multiple speakers:
speech = element speech { speaker+, content } speaker = attribute speaker { text }
The following XML, though, is not well formed:
<speech speaker="hamlet" speaker="ophelia">Hi, Polonius!</speech>
Fix this by removing the plus sign and listing the speakers in the attribute value, along the lines of:
<speech speaker="hamlet ophelia">Hi, Polonius!</speech>
text
The reserved word text
means zero or more textual characters
.
This has two perhaps surprising consequences:
text
in a
schema! For this reason, text
should never use the
?
or *
repetition indicators, since that would
mean that it was optional, and as long no text counts as text, it doesn’t
make sense to try to distinguish text has been omittedfrom
text is present, in the form of zero textual characters.
text
, rather than two instances. This means that
text
should never be followed by +
or
*
(again), since repetition is meaningless when something
cannot be repeated.The keyword text
, then, should never be followed by a repetition
indicator.
If multiple elements have the same content, you can define a pattern as equal to that content and then use the pattern when you define the elements that contain it.
The bad block below is not invalid Relax NG, but it is bad because it is
unnecessarily repetitive, and writing the same code more than once (in this case
the same content model three times) creates an opportunity for inconsistency.
The version below that is an improvement because it starts by defining the
keyword stuff
as representing a pattern that can then be used in
content models. Note that the first line does not define an element or an
attributes (those keywords are missing), so what it means is that you can use
the label you’ve just defined, stuff
, in a content model wherever
you might otherwise have written its expansion. The next three lines then use
that label in the content models. These two code blocks have the same meaning,
but the second one is better because it avoids the repetition.
beginning = element beginning { mixed { ( poet | exaggeration | stars )* } middle = element middle {mixed { ( poet | exaggeration | stars )* } ending = element ending { mixed { ( poet | exaggeration | stars )* }
stuff = mixed { ( poet | exaggeration | stars )* } beginning = element beginning {stuff} middle = element middle {stuff} ending = element ending {stuff}
<digit>
elements in the example below, we specify the
attributes first:
<digit type="money" unit="dollar">99999999</digit> digit = element digit {type, unit, xsd:decimal} type = attribute type {text} unit = attribute unit {text}
@xml:id
values are uniqueThe XML below has an error: the value time
is assigned to the
@xml:id
attribute for two different elements. If in your schema
you define @xml:id
as being of type xsd:ID
(and you
should), all @xml:id
values in the entire document must be
unique.
<types> <type xml:id="money"/> <type xml:id="time"/> <type xml:id="weight"/> <type xml:id="percent"/> <type xml:id="sport"/> <type xml:id="animal"/> <type xml:id="misc"/> </types> <units> <unit xml:id="dollar" n="1"/> <unit xml:id="mDollar" n="1000000"/> <unit xml:id="time" hr="1"/> <unit xml:id="dTime" hr="24"/> <unit xml:id="yTime" yr="1"/> </units>
You can use the same names for elements and attributes with different
definitions as long as you use different labels for them in your Relax
NG. In the example below, a <person>
element that is a child
of <persons>
has different attributes than a
<person>
element inside a
<p>
.
<persons> <person role="filmmaker" surname="Lynch" forename="David" xml:id="lynch"/> <-- more persons --> </persons> <!-- more XML content --> <answer>Yes, I like <person ref="lynch">David Lynch</person>.</answer>
In the Relax NG below, we use two different labels to define elements of
type <person>
, each with a different content model.
They have the same element name, but we use different labels for them
(person_list
and p_person
).
persons = element persons {person_list +} person_list = element person {role, surname, forename, id} surname = attribute surname {text} forename = attribute forename {text} id = attribute xml:id {xsd:ID} answer = element answer {mixed { p_person* } } p_person = element person {ref, text} ref = attribute ref {xsd:IDREF}
Comments in Relax NG and elsewhere
Surrounded by
<!-- comment goes here -->
May span multiple lines
#
Every line must be commented individually