schema(3tcl)
okay bestie, this is schema(3tcl) β the tdom schema validation page π it's a big one, so grab a snack
π NAME π
tdom::schema β creates a schema validation command β¨
πΈ SYNOPSIS πΈ
package require tdom
tdom::schema ?create? cmdName
β¨ DESCRIPTION β¨
Every single time you call this command, it makes you a brand new validation command. Think of it like summoning a little schema-boss of your very own π«Ά
A validation command comes with methods that let you:
- define a schema,
- validate XML data against that schema,
- post-validate a tDOM DOM tree against it β and, to some degree, other kinds of hierarchical data too π
Also (this is the slay part) a validation command may be used as the argument to the -validateCmd option of the dom parse and the expat commands, which means you get validation on top of whatever those commands already do. stacking features, we love to see it π
The methods of created commands are:
π prefixns π
prefixns ?prefixUriList?
This one controls the prefix (or abbreviation) to namespace URI mapping. Anywhere a namespace argument is expected in the schema command methods, the "prefix" can be used instead of the namespace URI. lowkey a glow-up for your typing.
- If the list maps the same prefix to different namespace URIs, the first one wins π
- If there is no such prefix, the namespace argument is used literally as the namespace URI.
- Called with no argument? It returns the current
prefixUriList. - Called with the empty string? Then any namespace URI arguments are used literally. This is the default.
π¦ defelement π¦
defelement name ?namespace? <definition script>
Defines the element name (optional in the namespace namespace) in the schema. The definition script is evaluated and defines the content model of the element. If the namespace argument is given, any element or ref references in the definition script that are not wrapped inside a namespace command get resolved in that namespace.
And if there's already an element definition for that name/namespace combo? It raises error. no duplicates, we don't do that here π€
π defelementtype π
defelementtype typename ?namespace? <definition script>
Defines the element type typename (optional in the namespace namespace) in the schema. If the element type is used in a definition script with the schema command element, the validation engine expects an element content according to the content model definition script.
Real talk: defining (and using) element types seems only sensible if you really have elements with the same name and namespace but different content models. That's the whole point of them π«
The definition script is evaluated and defines the content model of the element it is assigned to. If the namespace argument is given, any element or ref references in the definition script not wrapped inside a namespace command are resolved in that namespace. If there is already an elementtype definition for that typename/namespace combination, the command raises error.
π¨ Huge caveat, don't skip this one: the document element of any XML you want to validate cannot be a defelementtype-defined element. periodt.
π defpattern π
defpattern name ?namespace? <definition script>
Defines a (maybe complex) content particle with the name (optional in the namespace namespace) in the schema, so it can be used in other definition scripts with the definition command ref. The definition script is evaluated and defines the content model of the content particle.
If the namespace argument is given, any element or ref references in the definition script not wrapped inside a namespace command are resolved in that namespace. If there is already a pattern definition for that name/namespace combination, the command raises error. π«
π deftexttype π
deftexttype name <constraint script>
Defines a bundle of text constraints that can be referred to by name while defining constraints on text element or attribute values. If there's already a text type definition with this name, the command raises error.
β¨ cute little detail: a text type may be referred to before it is defined in the schema. And if a referred text type isn't defined anywhere in the schema at all, then literally any text will match this type during validation. vibes-based validation, iykyk π
π start π
start documentElement ?namespace?
Defines the name and namespace of the root element of a tree you want to validate. If this method is used, the root element must match for validity β no exceptions bestie.
If start is not used, any element defined by defelement may be the root of a valid document.
The start method may be used several times with varying arguments during the lifetime of a validation command. no commitment, we stan π
And if the command is called with just the empty string (and no namespace argument), the validation constraint for the root element is removed and any defined element will be valid as the root of a tree to validate. freedom π¦
π§ define π§
define <definition script>
Defines several elements or patterns, or a whole entire schema, in one single call β by evaluating the definition script. All the schema command methods we've met so far (prefixns, defelement, defelementtype, defpattern, deftexttype and start) are allowed at the top level in the definition script.
β οΈ But the define method itself is not allowed recursively. don't do inception, bestie.
π event π
event (start|end|text) ?event specific data?
This method enables validation of hierarchical data against the content constraints of the validation command.
start
start name ?attributes? ?namespace?
Checks if the current validation state allows the element name in the namespace to start here. It raises error if not. π€
end
end
Checks if the current innermost open element may end there in the current state without violating validation constraints. It raises error if not.
text
text text
Checks if the current validation state allows the given text content. It raises error if not.
π validate π
validate ?options? <XML string> ?objVar?
Returns true if the <XML string> is valid, or false otherwise. If validation has failed and the optional objVar argument is given, the variable with that name is set to a validation error message. If the XML string is valid and the optional objVar argument is given, the variable will be untouched. (not me checking a variable that never got set π)
The valid options are:
-baseurl<baseURI>β If-baseurl <baseURI>is specified, thebaseURIis used as the base URI of the document. External entities references in the document are resolved relative to this base URI. This base URI is also stored within the DOM tree.-externalentitycommand<script>β If-externalentitycommand <script>is specified, the specified Tcl script is called to resolve any external entities of the document. The default is"::tdom::extRefHandler", which is a simple file URL resolver defined by the script part of the package. Setting the option value to the empty string disables resolving of external entities. The actual evaluated command consists of this option followed by three arguments: the base uri, the system identifier of the entity and the public identifier of the entity. The base uri and the public identifier may be the empty list. The script has to return a Tcl list consisting of three elements.- The first element of this list signals how the external entity is returned to the processor. Currently the two allowed types are
"string"and"channel". - The second element of the list has to be the (absolute) base URI of the external entity to be parsed.
- The third element of the list are data β either the already read data out of the external entity as a string in the case of type
"string", or the name of a Tcl channel in the case of type"channel". - > β οΈ Note that if the script returns a Tcl channel, it will not be closed by the processor. It must be closed separately if it is no longer needed. handle your own cleanup bestie π«
- The first element of this list signals how the external entity is returned to the processor. Currently the two allowed types are
-paramentityparsing<always|never|notstandalone>β The-paramentityparsingoption controls whether the parser tries to resolve the external entities (including the external DTD subset) of the document while building the DOM tree.-paramentityparsingrequires an argument, which must be either"always","never", or"notstandalone"."always"means the parser tries to resolve (recursively) all external entities of the XML source. This is the default in case-paramentityparsingis omitted."never"means only the given XML source is parsed and no external entity (including the external subset) will be resolved and parsed."notstandalone"means that all external entities will be resolved and parsed, with the exception of documents which explicitly statestandalone="yes"in their XML declaration.
-useForeignDTD<boolean>β If<boolean>is true and the document does not have an external subset, the parser will call the-externalentitycommandscript with empty values for thesystemIdandpublicIDarguments. Please note that if the document also doesn't have an internal subset, the-startdoctypedeclcommandand-enddoctypedeclcommandscripts, if set, are not called.
πΈ validatefile πΈ
validatefile ?options? filename ?objVar?
Returns true if the content of filename is valid, or false otherwise. The given file is fed as a binary stream to expat, therefore heads up: only US-ASCII, ISO-8859-1, UTF-8 or UTF-16 encoded data will work with this method. pick your encoding wisely π«Ά
If validation has failed and the optional objVar argument is given, the variable with that name is set to a validation error message. If the XML data is valid and the optional objVar argument is given, the variable will be untouched.
The allowed options and their meaning are the same as for the validate method β go peek at that one for the full description π
π The Methods, Part Two (we're so back)
validatechannel ?options? channel ?objVar?
okay so this one reads from a Tcl channel and returns true if the content you pulled out of that channel is valid, or false if it's giving invalid. π
heads up bestie: since data read out of a Tcl channel is UTF-8 encoded, any misleading encoding declaration sitting at the beginning of the data will lead to errors. like, don't lie about your encoding, it never ends well. π
if the validation fails and you handed over the optional objVar argument, the variable with that name gets set to a validation error message. if the XML data is valid and you gave objVar, the variable stays untouched β not me leaving it alone, it's simply not its moment. β¨
the allowed options and their meaning are the same as for the validate method; go read there for the full description, iykyk.
domvalidate domNode ?objVar?
returns true if the first argument is a valid tree, or false otherwise. π
if validation has failed and the optional objVar argument is given, the variable with that name is set to a validation error message. if the dom tree is valid and objVar is given, that variable gets set to the empty string. (yes literally empty. iconic behavior honestly. π )
reportcmd ?cmd?
this method expects the name of a Tcl command to be called in case of a validation error. the command will be called with two arguments appended: the schema command which raises the validation error, and a validation error code. π
the possible error codes are:
MISSING_ELEMENTMISSING_TEXTUNEXPECTED_ELEMENTUNEXPECTED_ROOT_ELEMENTUNEXPECTED_TEXTUNKNOWN_ROOT_ELEMENTUNKNOWN_ATTRIBUTEMISSING_ATTRIBUTEINVALID_ATTRIBUTE_VALUEDOM_KEYCONSTRAINTDOM_XPATH_BOOLEANINVALID_KEYREFINVALID_VALUEUNKNOWN_GLOBAL_IDUNKNOWN_ID
for more detailed information see section Recovering. π
delete
this method deletes the validation command. bye bestie, block and delete. πβ¨
info ?args?
this method bundles methods to query the state of and details about the schema command. it's giving β¨control panelβ¨, it's giving dashboard energy. π¦
validationstate
this method returns the state of the validation command with respect to validation state. the possible return values and their meanings are:
READYβ the validation command is ready to start validation πΈVALIDATINGβ the validation command is in the process of validating input. π«ΆFINISHEDβ the validation has finished, no further events are expected. π
vstate
this method is a shorter alias for validationstate; see there. lowkey the same thing, highkey convenient. π
line
if the schema command is currently validating, this method returns the line part of the parsing position information, and the empty string in all other cases. if the schema command is currently post-validating a DOM tree, there may be no position information stored at some or all nodes. the empty string is returned in these cases. π
column
if the schema command is currently validating, this method returns the column part of the parsing position information, and the empty string in all other cases. if the schema command is currently post-validating a DOM tree, there may be no position information stored at some or all nodes. the empty string is returned in these cases. π
byteIndex
if the schema command is currently validating, this method returns the byte position of the parsing position information, and the empty string in all other cases. if the schema command is currently post-validating a DOM tree, there may be no position information stored at some or all nodes. the empty string is returned in these cases. π
domNode
if the schema command isn't currently post-validating a DOM tree, this method returns the empty string. otherwise, if the schema command waits for the reportcmd script to finish while recovering from a validation error, it returns the node on which the validation engine is currently looking at in case the node is an ELEMENT_NODE or, if not, its parent node. π³
β οΈ it is recommended that you do not use this method. or at least leave the DOM tree alone, use it read-only. like bestie pls don't touch, just look. π
nrForwardDefinitions
returns how many elements, element types and ref patterns are referenced that aren't defined so far (summed together). it's the "who are you even" counter fr fr. π
definedElements
returns in no particular order the defined elements in the grammar as list. if an element is namespaced, its list entry will be itself a list with two elements, with the name as first and the namespace as second element. π
definedElementtypes
returns in no particular order the defined element types in the grammar as list. if an element type is namespaced, its list entry will be itself a list with two elements, with the name as first and the namespace as second element. π
definedPatterns
returns in no particular order the defined named pattern in the grammar as list. if a named pattern is namespaced, its list entry will be itself a list with two elements, with the name as first and the namespace as second element. π
expected
returns in no particular order all possible next events (since the last successful event match, if there was one) as a list. β¨
- if an element is namespaced, its list entry will be itself a list with two elements, with the name as first and the namespace as second element.
- if text is a possible next event, the list entry will be a two elements list, with
#textas first element and the empty string as second. - if an
anyelement constraint is possible, the list entry will be a two elements list, with<any>as first element and the empty string as second. - if an
anyelement in a certain namespace constraint is possible, the list entry will be a two elements list, with<any>as first element and the namespace as second. - if element end is a possible event, the list entry will be a two elements list with
<elementend>as first element and the empty string as second element.
definition name ?namespace?
returns the code that defines the given element. the command raises error if there is no definition of that element. π¨
typedefinition name ?namespace?
returns the code that defines the given element type definition. the command raises error if there is no definition of that element. π¨
patterndefinition name ?namespace?
returns the code that defines the given pattern definition. the command raises error if there is no definition of a pattern with that name and, if given, namespace. π¨
vaction ?name|namespace|text?
this method returns useful information only if the schema command waits for the reportcmd script to finish while recovering from a validation error. otherwise it returns NONE. π
if the command is called without the optional argument, the possible return values and their meanings are:
NONEβ the schema command currently does not recover from a validation event.MATCH_ELEMENT_STARTβ element start event, which includes looking for missing or unknown attributes. πMATCH_ELEMENT_ENDβ element end event.MATCH_TEXTβ validating text between tags.MATCH_ATTRIBUTE_TEXTβ attribute text value constraint check.MATCH_GLOBALβ checking global IDs.MATCH_DOM_KEYCONSTRAINTβ checkingdomuniqueconstraint.MATCH_DOM_XPATH_BOOLEANβ checkingdomxpathbooleanconstant.
if called with one of the possible optional arguments, the command returns detail information depending on current action. π«Ά
nameβ returns the name of the element that has to match in case ofMATCH_ELEMENT_START. returns the name of the closed element in case ofMATCH_ELEMENT_END. returns the name of the attribute in case ofMATCH_ATTRIBUTE_TEXT. returns the name of the parent element in case ofMATCH_TEXT.namespaceβ returns the namespace of the element that has to match in case ofMATCH_ELEMENT_START. returns the namespace of the closed element in case ofMATCH_ELEMENT_END. returns the namespace of the attribute in case ofMATCH_ATTRIBUTE_TEXT. returns the namespace of the parent element in case ofMATCH_TEXT.textβ returns the text to match in case ofMATCH_TEXT. returns the value of the attribute in case ofMATCH_ATTRIBUTE_TEXT.
stack top|inside|associated
in Tcl scripts evaluated by validation, this method provides information about the current validation stack. called outside this context the method returns the empty string. π₯²
topβ returns the element whose content is currently checked (the open element tag at this moment).insideβ returns all currently open elements as a list.associatedβ returns the data associated with the current top most stack content particle or the empty string if there isn't any. πresetβ this one resets the validation command right back into stateREADY, while preserving the grammar you already defined. Your grammar is safe, bestie, nothing gets yeeted π
π Schema definition scripts π
Okay so schema definition scripts are literally just ordinary Tcl scripts, and they get evaluated in the namespace tdom::schema. All the commands listed below live in that Tcl namespace, and they let you define a whole rainbow of document structures π. Every single schema definition command sets up a validation constraint on the content β and that content either has to match, or it has to be optional, for it to qualify as valid. And if there's extra content that didn't get matched? That is a validation error, periodt. Also, white-space-only text (in the XML sense of white space, iykyk) between any different tags gets ignored β with the one exception of text only elements, where even white-space-only text counts as significant content. πΈ
The schema definition commands are:
element name ?quant? (?<definition script>|βtypeβ typename)?
- If neither the optional definition script argument nor the string
"type"plus a typename is given, this command refers to the element defined withdefelementunder the name name in the current context namespace. π - If the string
"type"and a typename are given, then the content of the element is described by the content model defined withdefelementtypeunder the name typename in the current context namespace. - If the defelement script argument is given, the validation constraint expects an element with the name name in the current namespace, with content "locally" defined by the definition script. Forward references to elements or patterns that haven't been defined yet β or other local definitions of the same name inside the definition script β are allowed, no stress π«Ά. But if a forward referenced element still isn't defined by the time validation rolls around, then only an empty element with name name and namespace namespace and no attributes matches.
ref name ?quant?
This command refers to the content particle defined with defpattern under the name name in the current context namespace. Forward references to a pattern that isn't defined yet and recursive references are both allowed, iconic behaviour honestly. If a forward referenced pattern still isn't defined by validation time, then no content whatsoever is expected β the "empty match". β¨
group ?quant? <definition script>
This method groups a sequence of content particles defined by the definition script, and they all have to match in this sequence order. In order. No shuffling. π
choice ?quant? <definition script>
This schema constraint matches if one of the top level content particles defined by the definition script matches. And if one of these top level content particles is optional, then this constraint matches the "empty match". Pick your fighter π
interleave ?quant? <definition script>
This schema constraint matches after every one of the required top level content particles defined by the definition script has matched (and, optionally, some or all of the other ones), in any arbitrary order. Chaos mode: enabled. πͺοΈ
mixed ?quant? <definition script>
This schema constraint matches for any text (including the empty one) and every top level content particle defined by the definition script with default quantifier *. She's flexible like that. π¦
text ?<constraint script>|βtypeβ typename?
Without the optional constraint script, this validation constraint matches every string (including the empty one). With a constraint script, or with a given text type argument, a text matching this script or the text type is expected. π
any ?options? ?<namespace list>? ?quant?
Without arguments, the any command matches every element. If the <namespace list> argument is given, this matches any element in a namespace out of that list. The empty string means elements with no namespace. If additionally the option -not is given, then this matches every element with a namespace not in the list. The only other recognized option is --, which signals the end of any options.
Please note that in the case of no namespace argument being given, that means the quantifiers*and+will eat up any elements until the enclosing element ends. And if you really have a namespace that looks like a valid tDOM schema quantifier, you will have to spell out always both arguments. Yes, both. Every time. π
attribute name ?quant? (?<constraint script>|βtypeβ typename?)
The attribute command defines an attribute (in no namespace) to the enclosing element. The first definition of name inside an element definition wins β later definitions of the same name are silently ignored, she said what she said π. After the name argument there may be one of the quantifiers ? or !. If there is, it will be used. Otherwise the attribute will be required (so it must be present in the XML source). If there is one argument more, that argument is evaluated as a constraint script, defining the value constraints of the attribute. Otherwise, if there are two more arguments and the first of them is the bare-word "type", the following argument is used as a text type name.
This command is only allowed at top level in the definition script of adefelement/elementscript.
nsattribute name namespace ?quant? (?<constraint script>|βtypeβ typename?)
This command does the same as the command attribute, but for the attribute name in the namespace namespace. Same energy, different namespace π
namespace URI <definition script>
Evaluates the definition script with context namespace URI. Every element, element type or ref command name will be looked up in the namespace URI, and locally defined elements will be in that namespace. An empty string as URI means no namespace. π«§
tcl tclcmd ?arg arg ...?
Evaluates the Tcl script tclcmd arg arg .... This validation command is only allowed in strict sequential context β so not in choice, mixed and interleave. If the return code is something else than TCL_OK, this is an error (which is not caught and reported by reportcmd). β οΈ
self
Returns the schema command. Short. Sweet. Iconic. π
associate data
This command is only allowed top-level inside definition scripts of the element, elementtype, pattern or interleave content particles. It associates the data given as argument with the currently defined content particle, and it may be requested in scripts evaluated while validating the content of that particle with the schema command method call info stack associated. π§
domunique selector fieldlist ?name? ?βIGNORE_EMPTY_FIELD_SETβ|(βEMPTY_FIELD_SET_VALUEβ emptyFieldSetValue)?
If not postvalidating a DOM tree with domvalidate, this constraint always matches. If postvalidating, this constraint resembles the xsd key/keyref mechanism. The selector argument may be any valid XPath expression (without the xsd limits). Several domunique commands within one element definition are allowed. They are checked in definition order. The argument name is available in the recovering script per info vaction name. If the fieldlist does not select something for a node of the result set of the selector, the key value will be the empty string by default. If the arguments EMPTY_FIELD_SET_VALUE <value> are given, an empty node set will have the key value value. If instead the flag IGNORE_EMPTY_FIELD_SET flag is given, an empty node set result will not have any key value. π
domxpathboolean XPath_expr ?name?
If not postvalidating a DOM tree with domvalidate, this constraint always matches. If postvalidating, the XPath_expr argument is evaluated (with the node matching the schema parent of the domxpathboolean command as context node). The constraint matches if the result of this XPath expression, converted to boolean by XPath rules, is true. Several domxpathboolean commands within one element definition are allowed. They are checked in definition order.
This enables checks depending on more than one element. Consider:
tdom::schema s
s define {
defelement doc {
element a ! text
element b ! text
element c ! text
domxpathboolean "a * b * c >= 20000" volume
domxpathboolean "a > b and b > c" sequence
}
}
jsontype JSON structure type
If not postvalidating a DOM tree with domvalidate, this constraint always matches. If postvalidating, the constraint matches if the enclosing element has the JSON type given as argument to the structure constraint. The possible JSON structure types are NONE, OBJECT and ARRAY. This constraint is only allowed as direct child of a defelement, defelementtype or local element definition. π₯
prefixns ?prefixUriList?
This defines a prefix to namespace URI mapping exactly as a schemacmd prefixns would. It is meant as a top-level command of a schemacmd define script.
This command is not allowed nested in another definition script command, and it will raise an error if you call it there.
defelement name ?namespace? <definition script>
This defines an element exactly as a schemacmd defelement call would. It is meant as a top-level command of a schemacmd define script.
This command is not allowed nested in another definition script command, and it will raise an error if you call it there.
defelementtype typename ?namespace? <definition script>
This defines an elementtype exactly as a schemacmd defelementtype call would. It is meant as a top-level command of a schemacmd define script.
This command is not allowed nested in another definition script command, and it will raise an error if you call it there.
defpattern name ?namespace? <definition script>
This defines a named pattern exactly as a schemacmd defpattern call would. It is meant as a top-level command of a schemacmd define script.
This command is not allowed nested in another definition script command, and it will raise an error if you call it there.
deftexttype name <constraint script>
This defines a named bundle of text constraints exactly as a schemacmd deftexttype call would. It is meant as a top-level command of a schemacmd define script.
This command is not allowed nested in another definition script command, and it will raise an error if you call it there.
start name ?namespace?β bestie, this one works exactly like aschemacmd startcall would. It is meant as the top-level command of aschemacmd definescript. Not allowed nested in another definition script command, and it will raise error if you call it there. Periodt. π
β¨ Quantity specifier β¨
Several schema definition commands expect a quantifier as one of their arguments, which determines how often the content particle specified by the command is expected. The valid values for a quant argument are:
!β the content particle has to occur exactly once in valid documents. One and done. π―?β the content particle may not occur more than once in valid documents β the particle is optional. Zero or one, no more. π*β the content particle may occur zero or more times in a row in valid documents. Go off, do it as many times as you want. β¨+β the content particle may occur one or more times in a row in valid documents. At least one, bestie. πͺnβ the content particle must occurntimes in a row in valid documents. The quantifier must be an integer greater zero. No zero, no negatives. π«{n m}β the content particle must occur at leastnand at mostmtimes in a row in valid documents. The quantifier must be a Tcl list with two elements. The first element of this list must be an integer withn >= 0. If the second list element is the character*, then there is no upper limit. Otherwise the second list element must be an integer withn < m. So{0 *}is giving unlimited,{2 5}is giving strict. π
If an optional quantifier is not given, it defaults to * in case of the mixed command and to ! for all other commands. That's the default vibe. π
β¨ Text constraint scripts β¨
Text (parsed character data, as XML calls it) sometimes has to be of a certain kind or comply with certain rules to be valid. The text constraint script arguments to text, attribute, nsattribute and deftexttype commands are evaluated in the Tcl namespace tdom::schema::text namespace and allow the ensuing text constraint commands to check text for certain properties. The commands are defined in the Tcl namespace tdom::schema::text. They raise error in case they are called outside of a text constraint script. No sneaky calls, bestie. π«
A few of the ensuing text type commands are exposed as general Tcl commands. They are defined in the namespace tdom::type and are called as documented below with the text to check appended to the argument list. They return a logical value. Please note that the commands may not accept starting or ending white space. Whether a command is available in the tdom::type namespace is recorded in its documentation. iykyk. β¨
β¨ The tcl text constraint command β¨
The tcl text constraint command dispatches the check to an arbitrary Tcl command, thus enabling any programmable decision rules. Main character energy: you decide. π
tcl tclcmd ?arg arg ...?β Evaluates the Tcl scripttclcmd arg arg ...and the text to validate appended to the argument list. The return value of the Tcl command is interpreted as a boolean. So yes/no, true/false, slay/not slay. π
β¨ Basic XML types β¨
nameβ<URL: https://www.w3.org/TR/xml/#NT-Name>This text constraint matches if the text value matches the XML name production . This means that the text value must start with a letter, underscore (_), or colon (:), and may contain only letters, digits, underscores (_), colons (:), hyphens (-), and periods (.). Iconic XML name behavior. π·οΈncnameβ<URL: https://www.w3.org/TR/xml-names/#NT-NCName>This text constraint matches if the text value matches the XML ncname production . This means that the text value must start with a letter or underscore (_), and may contain only letters, digits, underscores (_), hyphens (-), and periods (.). The only difference to thenameconstraint is that colons are not permitted. No colon, no problem. π«qnameβ<URL: https://www.w3.org/TR/xml-names/#NT-QName>This text constraint matches if the text value matches the XML qname production . This means that the text value is either ancnameor twoncnames joined by a colon (:). Likeprefix:local, periodt. πnmtokenβ<URL: https://www.w3.org/TR/xml/#NT-Nmtoken>This text constraint matches if the text value matches the XML nmtoken production . Yes, that's the whole vibe. β¨nmtokensβ<URL: https://www.w3.org/TR/xml/#NT-Nmtokens>This text constraint matches if the text value matches the XML nmtokens production . Plural, more tokens, more fun. π«
β¨ Basic type tests β¨
integer ?(xsd|tcl)?β This text constraint matches if the text value could be parsed as an integer. If the optional argument to the command istcl, everything that returnsTCL_OKif fed intoTcl_GetInt()matches. If the optional argument to the command isxsd, the constraint matches if the value is a validxsd:integer. Without argumentxsdis the default. Soxsdunless you say otherwise. π’negativeInteger ?(xsd|tcl)?β This text constraint matches the same text values as theintegertext constraint (see there), with the additional constraint that the value must be< zero. Negative vibes only. πnonNegativeInteger ?(xsd|tcl)?β This text constraint matches the same text values as theintegertext constraint (see there), with the additional constraint that the value must be>= zero. Zero is allowed, bestie. π«ΆnonPositiveInteger ?(xsd|tcl)?β This text constraint matches the same text values as theintegertext constraint (see there), with the additional constraint that the value must be<= zero. Zero and down. β¬οΈpositiveInteger ?(xsd|tcl)?β This text constraint matches the same text values as theintegertext constraint (see there), with the additional constraint that the value must be> zero. Strictly above zero. β¨number ?(xsd|tcl)?β This text constraint matches if the text value could be parsed as a number. If the optional argument to the command istcl, everything that returnsTCL_OKif fed intoTcl_GetDouble()matches. If the optional argument to the command isxsd, the constraint matches if the value is a validxsd:decimal. Without argumentxsdis the default. Numbers, but make it XSD. π’boolean ?(xsd|tcl)?β This text constraint matches if the text value could be parsed as a boolean. If the optional argument to the command istcl, everything that returnsTCL_OKif fed intoTcl_GetBoolean()matches. If the optional argument to the command isxsd, the constraint matches if the value is a validxsd:boolean. Without argumentxsdis the default. True/false, yes/no, slay/not slay. βdateβ This text constraint matches if the text value is axsd:date, which is basically like an ISO 8601 date of the formYYYY-MM-DD, with optional time zone part (either the letterZor plus (+) or minus (-) followed byhh:mmand with maximum allowed positive or negative time zone14:00). It follows the date rules of the Gregorian calendar for all dates. A preceding minus sign for bce dates is allowed. There is no year 0. The year may have more than 4 digits, but only if needed (no extra leading zeros). This is available as common Tcl commandtdom::type::date. Calendar girl, but make it XML. πtimeβ This text constraint matches if the text value is axsd:time, which is basically like an ISO 8601 time of the formhh:mm:sswith optional time zone part. The time zone part follows the rules of thedatecommand; see there. All three parts of the time value (hours, minutes, seconds) must be spelled out with 2 digits. Additional fractional seconds (with a point (.) as separator) are allowed, but not just a dangling point. The time value24:00:00(without fractional part) is allowed. This is available as common Tcl commandtdom::type::time. Time is fake, but this constraint is real. β°dateTimeβ This text constraint matches if the text value is axsd:dateTime, which is basically like an ISO 8601 date time of the formYYYY-MM-DDThh:mm:sswith optional time zone part. The date and time zone parts follow the rules of thedateandtimecommand; see there. The time part (including the signaling'T'character) is mandatory. This is available as common Tcl commandtdom::type::dateTime. Date plus time, the full package. πβ±οΈdurationβ This text constraint matches if the text value is axsd:duration, which is basically like an ISO 8601 duration of the formPnYnMnDTnHnMnS. All parts other than the startingPand β if one ofH,MorSis given βTare optional. In case the following sign letter isS,nmay be a decimal (with at least one digit before and after the dot), otherwise it must be a (positive) integer. This is available as common Tcl commandtdom::type::duration. Time is a construct, but this constraint is valid. β³base64β This text constraint matches if text is valid according to RFC 4648. Base64 bestie, we love her. π§¬hexBinaryβ This text constraint matches if text is a sequence of binary octets in hexadecimal encoding, where each binary octet is a two-character hexadecimal number. Lowercase and uppercase lettersAthroughFare permitted. Hex girl summer. π₯unsignedByteβ This text constraint matches if the text value is axsd:unsignedByte. This is an integer between0and255, both included, optionally preceded by a+sign and leading zeros. Small but unsigned. π£unsignedShortβ This text constraint matches if the text value is axsd:unsignedShort. This is an integer between0and65535, both included, optionally preceded by a+sign and leading zeros. Short queen. πunsignedIntβ This text constraint matches if the text value is axsd:unsignedInt. This is an integer between0and4294967295, both included, optionally preceded by a+sign and leading zeros. Int but make it unsigned. πunsignedLongβ This text constraint matches if the text value is axsd:unsignedLong. This is an integer between0and18446744073709551615, both included, optionally preceded by a+sign and leading zeros. Long and unsigned, a whole vibe. πbyteβ This text constraint matches if the text value is axsd:byte. This is an integer between-128and127, both included, optionally preceded by a+or a-sign and leading zeros. Tiny but signed. π§Έshortβ This text constraint matches if the text value is axsd:short. This is an integer between-32768and32767, both included, optionally preceded by a+or a-sign and leading zeros. Short and signed. β¨intβ This text constraint matches if the text value is axsd:int. This is an integer between-2147483648and2147483647, both included, optionally preceded by a+or a-sign and leading zeros. Classic int behavior. π―longβ This text constraint matches if the text value is axsd:long. This is an integer between-9223372036854775808and9223372036854775807, both included, optionally preceded by a+or a-sign and leading zeros. Long long, signed, slay. π
Logical constructs β¨
Okay bestie, these are the big brain combinators. They let you mix and match other text constraints like it's a playlist. π
oneOf <constraint script>β matches if one of the text constraints defined in the argument constraint script matches the text. It stops after the first match and probes the text constraints in the order of definition. So first W wins, no cap. πallOf <constraint script>β matches if all of the text constraints defined in the argument constraint script match the text. It stops after the first match failure and probes the text constraints in the order of definition. Since the schema definition commandtextalso expects all text constraints to match the text constraint,allOfis useful mostly in connection with theoneOftext constraint command. So it's giving "everybody has to pass", and it's lowkey a sidekick tooneOf. β¨not <constraint script>β matches if none of the text constraints defined in the argument constraint script matches the text. It stops after the first matching constraint in the constraint script and reports a validation error. The text constraints in the constraint script are probed in the order of definition. So it's the "nope, absolutely not" vibe. π«type text type nameβ matches if the text type given as argument matches. Simple as that. π
Constraints on processed text value π§Ό
These ones clean up or slice up the text before checking it. Self-care for strings, honestly.
whitespace (preserve|replace|collapse) <constraint script>β does white-space normalization to the text value and checks the resulting text with the text constraints of the constraint script argument. The white-space characters are#x20(space,' '),#x9(tab,\t),#xA(linefeed,\n), and#xD(carriage return,\r). The normalization methodpreservekeeps everything as it is; this is another way to sayallOf. Thereplacenormalization method replaces any single white-space character (as above) to a space. Thecollapsenormalization method removes all leading and trailing white-space, and all the other sequences of contiguous white-space are replaced by a single space. Sopreserveis "leave my baby alone",replaceis "one space per space", andcollapseis "we're Marie Kondo-ing this whitespace". π§Ήsplit ?type ?args??<constraint script>β splits the text to test into a list of values and tests all elements of that list for the text constraints in the evaluated constraint script. The available types are:whitespaceβ the text to split is stripped of all white space at start and end and split into a list at any successive white space. Default behavior, basically.tcl tclcmd ?arg ...?β the text to split is handed to thetclcmd, which is evaluated on global level, appended with every givenargand the text to split as last argument. This call must return a valid Tcl list whose elements are tested. So you can bring your own splitter. π οΈ- The default in case no split type argument is given is
whitespace. Periodt.
strip <constraint script>β tests all text constraints in the evaluated constraint script with the text to test stripped of all white space at start and end. It's the "trim first, ask questions later" energy. βοΈ
Various other string properties π
Just a whole bag of checks. Pick your fighter.
fixed valueβ only matches if the text value is string equal to the givenvalue. Exact match, no notes.enumeration listβ matches if the text value is equal to one element (respecting case and any white-space) of the argumentlist, which has to be a valid Tcl list. So it's a VIP list, and case/whitespace still matter. πmatch ?-nocase? glob_style_match_pattern>β matches if the text value matches the glob style pattern given as argument. It follows the rules of the Tcl[string match]command, see<URL: https://www.tcl.tk/man/tcl8.6/TclCmd/string.htm#M35>. (Yes, the trailing>in the syntax is in the original, so we're keeping it exactly. π)regexp expressionβ matches if the text value matches the regular expression given as argument.<URL: https://www.tcl.tk/man/tcl8.6/TclCmd/re_syntax.htm>describes the regular expression syntax. Regex girlies, this one's for you. π₯length lengthβ matches if the length of the text value (in characters, not bytes) islength. Thelengthargument must be a positive integer or zero. So zero is allowed, bestie.maxLength lengthβ matches if the length of the text value (in characters, not bytes) is at mostlength. Thelengthargument must be an integer greater zero.minLength lengthβ matches if the length of the text value (in characters, not bytes) is at leastlength. Thelengthargument must be an integer greater zero.id ?keySpace?β marks the text as a document wide ID (to be referenced by anidref). Every ID value within a document must be unique. It isn't an error if the ID isn't actually referenced within the document. The optional argumentkeySpacedoes all this for a named key space. The key space""(the empty sting) is another key space then theidcommand withoutkeySpaceargument. (We're keeping "sting" because that's what the original says. π«Ά)idref ?keySpace?β expects the text to be a reference to an ID within the document. The referenced ID may appear later in the document than the reference. Several references within the document to one ID are possible. For the optionalkeySpaceargument seeid. So it's the "I'll find them eventually" reference. πjsontype <JSON text type>β if not postvalidating a DOM tree withdomvalidatethis constraint always matches. If postvalidating the currentTEXT_NODEto check must have the JSON text type given as argument to the text constraint command. The possible types areNULL,TRUE,FALSE,STRINGandNUMBER. JSON besties, this is your moment. π
Local key constraints π
Document wide uniqueness and foreign key constraints are available with the text constraint commands id and idref. Keyspaces allow for sub-tree local uniqueness and foreign key constraints. So yes, you can have local drama, not just document-wide drama. π
keyspace <names list> <constraint script>β any number of keyspaces are possible. A keyspace is either active or not. Callingkeyspaceinside a constraint script with the same name does nothing.- These text constraint commands work with keyspaces:
key <name>β if the keyspace with the name<name>is not active the constraint always matches. If the keyspace is active, reports error if there is already a key with the value. Otherwise it stores the value as key in this keyspace and matches. So it's "first come, first served" inside that keyspace. ποΈkeyref <name>β if the keyspace with the name<name>is not active it always matches. If the keyspace is active then reports error if there is still no key as the value at the end of the keyspace<name>. Otherwise, it matches. It's the "where's the key bestie?" check. π
Recovering π©Ή
By default the validation engine stops at the first detected validation violation and reports that finding. It does so by returning false (and sets, if given, the result variable with an error message) in case the schema command itself is used to validate input. If the schema command is used by a SAX parser or the DOM parser, it does so by throwing error. So default mode is "we are NOT okay, shutting down." π
If a reportcmd is set this command is called on global level appended with the schema command and an error type as arguments in case a validation violation is detected. Then the validation recovers from the error and continues. For some validation errors the recover strategy can be determined with the script result of the reportcmd. So reportcmd is your little recovery bestie. π«Ά
With a reportcmd (as long as the reportcmd does not throw error while called) the validation engine will never report validation failure to its caller. The validation engine recovers, continues, and reports the next error (if occurring) and so on until the end of the input. The schema command will return true and the SAX parser and DOM builder will process normally until the end of the input, as if there had not been a validation error. It's giving "we move." π
Please note that this happens only for validation errors. It is not possible to recover from well-formedness errors. If the input is not well-formed, the schema command returns false and sets (if given) the result variable with an error message about the well-formedness error. So no recovery for malformed input, periodt. π«
If the reportcmd throws error while called by the validation engine then validation stops and the schema command throws error with the error message of the script. So if your recovery script itself breaks, everything stops. π
While validating basically three events can happen: an element start tag has to match, a piece of text has to match or an element end tag has to match. The method info vaction called in the recovering script or any script code called from there returns which event has triggered the error report (MATCH_ELEMENT_START, MATCH_TEXT, MATCH_ELEMENT_END, respectively). While the command walks through the schema looking whether the event matches other, data driven events (as, for example checking, if any keyref within a keyspace exists) may happen. So info vaction tells you what kind of drama is happening. π¬
Several of the validation error codes, appended as second argument to the reportcmd calls, may happen at more than one kind of validation event. The info vaction method and its subcommands provide information about the current validation event, if called from the report command. So use it, bestie. π
If a structural validation error happens, the default recovering strategy is to ignore any following (or missing) content within the current subtree and to continue with the element end event of the subtree. So we skip to the end of that subtree. π¦
Returning "ignore" from the recovering script in case of error type MISSING_ELEMENT recovers by ignoring the failed constraint and continues to match the event further against the schema. So "ignore" means "not today, keep going." π
Returning "vanish" from the recover script in case of the error types MISSING_ELEMENT and UNEXPECTED_ELEMENT recovers by ignoring the event. So "vanish" means "pretend it didn't happen." π¨
π Examples π
Okay bestie, this is the part where we look at actual real-world schemas and translate them, it's giving "show don't tell" and honestly? Iconic. π
<URL: https://www.w3.org/TR/xmlschema-0/> The XML Schema Part 0: Primer Second Edition () starts with this example schema:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:annotation>
<xsd:documentation xml:lang="en">
Purchase order schema for Example.com.
Copyright 2000 Example.com. All rights reserved.
</xsd:documentation>
</xsd:annotation>
<xsd:element name="purchaseOrder" type="PurchaseOrderType"/>
<xsd:element name="comment" type="xsd:string"/>
<xsd:complexType name="PurchaseOrderType">
<xsd:sequence>
<xsd:element name="shipTo" type="USAddress"/>
<xsd:element name="billTo" type="USAddress"/>
<xsd:element ref="comment" minOccurs="0"/>
<xsd:element name="items" type="Items"/>
</xsd:sequence>
<xsd:attribute name="orderDate" type="xsd:date"/>
</xsd:complexType>
<xsd:complexType name="USAddress">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="street" type="xsd:string"/>
<xsd:element name="city" type="xsd:string"/>
<xsd:element name="state" type="xsd:string"/>
<xsd:element name="zip" type="xsd:decimal"/>
</xsd:sequence>
<xsd:attribute name="country" type="xsd:NMTOKEN"
fixed="US"/>
</xsd:complexType>
<xsd:complexType name="Items">
<xsd:sequence>
<xsd:element name="item" minOccurs="0" maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="productName" type="xsd:string"/>
<xsd:element name="quantity">
<xsd:simpleType>
<xsd:restriction base="xsd:positiveInteger">
<xsd:maxExclusive value="100"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="USPrice" type="xsd:decimal"/>
<xsd:element ref="comment" minOccurs="0"/>
<xsd:element name="shipDate" type="xsd:date" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="partNum" type="SKU" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<!-- Stock Keeping Unit, a code for identifying products -->
<xsd:simpleType name="SKU">
<xsd:restriction base="xsd:string">
<xsd:pattern value="\d{3}-[A-Z]{2}"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>
A simple one-to-one translation of that into a tDOM schema definition script would be:
tdom::schema schema
schema define {
# Purchase order schema for Example.com.
# Copyright 2000 Example.com. All rights reserved.
defelement purchaseOrder {ref PurchaseOrderType}
foreach elm {comment name street city state product} {
defelement $elm text
}
defpattern PurchaseOrderType {
element shipTo ! {ref USAddress}
element billTo ! {ref USAddress}
element comment ?
element items
attribute orderDate date
}
defpattern USAddress {
element name
element street
element city
element state
element zip ! {text number}
attribute country {fixed "US"}
}
defelement items {
element item * {
element product
element quantity ! {text positiveInteger}
element USPrice ! {text number}
element comment
element shipDate ? {text date}
attribute partNum {regexp "^\d{3}-[A-Z]{2}$"}
}
}
}
<URL: http://relaxng.org/tutorial-20011203.html> The RELAX NG Tutorial () starts with this example:
Consider a simple XML representation of an email address book:
<addressBook>
<card>
<name>John Smith</name>
<email>js@example.com</email>
</card>
<card>
<name>Fred Bloggs</name>
<email>fb@example.net</email>
</card>
</addressBook>
The DTD would be as follows:
<!DOCTYPE addressBook [
<!ELEMENT addressBook (card*)>
<!ELEMENT card (name, email)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT email (#PCDATA)>
]>
A RELAX NG pattern for this could be written as follows:
<element name="addressBook" xmlns="http://relaxng.org/ns/structure/1.0">
<zeroOrMore>
<element name="card">
<element name="name">
<text/>
</element>
<element name="email">
<text/>
</element>
</element>
</zeroOrMore>
</element>
This schema definition script will do the same:
tdom::schema schema
schema define {
defelement addressBook {
element card *
}
defelement card {
element name
element email
}
foreach e {name email} {
defelement $e text
}
}
π KEYWORDS π
Validation, Postvalidation, DOM, SAX
And that's a wrap bestie!! π«Ά You just read the entire schema(3tcl) page and honestly? Main character energy, no notes. π This lil masterpiece ships with Tcl as schema(3tcl), so go forth and validate everything β your XML has never been this safe, periodt. β¨π
π¦ where this page comes from
- ubuntu resolute: tdom 0.9.6-2 Β· MPL-2.0
- ubuntu stonking: tdom 0.9.6-3 Β· MPL-2.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms