RELAX: Schemas Don't Have to be HardElliotte Rusty HaroldWednesday, March 21, 2007elharo@metalab.unc.eduhttp://www.cafeconleche.org/ |
Generically, a document that describes what a correct document may contain
Specifically, a very complex and baroque W3C Recommendation for an XML-document syntax that describes the permissible contents of XML documents
But there other options!
Created by James Clark and Murata Makoto
Based on TREX and RELAX
Formal theory is hedge automata
ISO standard: ISO/IEC 19757-2:2002(E), Document Schema Definition Languages (DSDL) — Part 2:Regular-grammar-based validation — RELAX NG, I
Unusual, non-XML like syntax
No data typing, especially for element content
Limited extensibility
Only marginally compatible with namespaces
Cannot use mixed content and enforce order and number of child elements
Cannot enforce number of child elements without also enforcing order.
(i.e. no &
operator from SGML)
Confuses infoset augmentation with validation
Complex, hard to understand
Not sufficiently extensible:
Checksums
SKUs match database
sum of item prices equals total price
Poorly implemented
Confuses infoset annotation with validation
<?xml version="1.0"?>
<GREETING>
Hello XML!
</GREETING>
<?xml version="1.0"?>
<element name="GREETING" xmlns="http://relaxng.org/ns/structure/1.0">
<text/>
</element>
$ jing greeting.rng greeting.xml
<?xml version="1.0"?>
<GREETING>
<P>Hello XML!</P>
</GREETING>
$ jing greeting.rng greeting3.xml /Users/elharo/Documents/speaking/sd2005west/relaxng/examples/greeting3.xml: 3:6: error: unknown element "P"
Notice how completely decoupled the validation is from the instance document. We can validate any document against any schema. We do not need to specify the schema in the instance document as you must do with DTDs and often need to do with W3C schemas.
Jing (Java): http://www.thaiopensource.com/relaxng/jing.html
libxml2 (C): http://xmlsoft.org/
Sun's Multischema Validator (Java): http://wwws.sun.com/software/xml/developers/multischema/
Tenuto (.NET): http://sourceforge.net/projects/relaxng
element GREETING { text }
<?xml version="1.0"?>
<SONG>
<TITLE>Hot Cop</TITLE>
<COMPOSER>Jacques Morali</COMPOSER>
<COMPOSER>Henri Belolo</COMPOSER>
<COMPOSER>Victor Willis</COMPOSER>
<PRODUCER>Jacques Morali</PRODUCER>
<PUBLISHER>PolyGram Records</PUBLISHER>
<LENGTH>6:20</LENGTH>
<YEAR>1978</YEAR>
<ARTIST>Village People</ARTIST>
</SONG>
<?xml version="1.0"?>
<element name="SONG" xmlns="http://relaxng.org/ns/structure/1.0">
<element name="TITLE">
<text/>
</element>
<element name="COMPOSER">
<text/>
</element>
<element name="COMPOSER">
<text/>
</element>
<element name="COMPOSER">
<text/>
</element>
<element name="PRODUCER">
<text/>
</element>
<element name="PUBLISHER">
<text/>
</element>
<element name="LENGTH">
<text/>
</element>
<element name="YEAR">
<text/>
</element>
<element name="ARTIST">
<text/>
</element>
</element>
element SONG {
element TITLE { text },
element COMPOSER { text },
element COMPOSER { text },
element COMPOSER { text },
element PRODUCER { text },
element PUBLISHER { text },
element LENGTH { text },
element YEAR { text },
element ARTIST { text }
}
zeroOrMore
oneOrMore
optional
<?xml version="1.0"?>
<element name="SONG" xmlns="http://relaxng.org/ns/structure/1.0">
<element name="TITLE">
<text/>
</element>
<oneOrMore>
<element name="COMPOSER">
<text/>
</element>
</oneOrMore>
<zeroOrMore>
<element name="PRODUCER">
<text/>
</element>
</zeroOrMore>
<optional>
<element name="PUBLISHER">
<text/>
</element>
</optional>
<optional>
<element name="LENGTH">
<text/>
</element>
</optional>
<optional>
<element name="YEAR">
<text/>
</element>
</optional>
<oneOrMore>
<element name="ARTIST">
<text/>
</element>
</oneOrMore>
</element>
element SONG {
element TITLE { text },
element COMPOSER { text }+,
element PRODUCER { text }*,
element PUBLISHER { text }?,
element LENGTH { text }?,
element YEAR { text }?,
element ARTIST { text }+
}
Boundary white space does not need to be declared
<?xml version="1.0"?>
<SONG>
<TITLE>Hot Cop</TITLE>
<COMPOSER>
<NAME>
<GIVEN>Jacques</GIVEN>
<FAMILY>Morali</FAMILY>
</NAME>
</COMPOSER>
<COMPOSER>
<NAME>
<GIVEN>Henri</GIVEN>
<FAMILY>Belolo</FAMILY>
</NAME>
</COMPOSER>
<COMPOSER>
<NAME>
<GIVEN>Victor</GIVEN>
<FAMILY>Willis</FAMILY>
</NAME>
</COMPOSER>
<PRODUCER>
<NAME>
<GIVEN>Jacques</GIVEN>
<FAMILY>Morali</FAMILY>
</NAME>
</PRODUCER>
<PUBLISHER>PolyGram Records</PUBLISHER>
<YEAR>1978</YEAR>
<ARTIST>Village People</ARTIST>
</SONG>
All of these structures can nest straight-forwardly
<?xml version="1.0"?>
<element name="SONG" xmlns="http://relaxng.org/ns/structure/1.0">
<element name="TITLE">
<text/>
</element>
<oneOrMore>
<element name="COMPOSER">
<element name="NAME">
<element name="GIVEN">
<text/>
</element>
<element name="FAMILY">
<text/>
</element>
</element>
</element>
</oneOrMore>
<zeroOrMore>
<element name="PRODUCER">
<element name="NAME">
<element name="GIVEN">
<text/>
</element>
<element name="FAMILY">
<text/>
</element>
</element>
</element>
</zeroOrMore>
<optional>
<element name="PUBLISHER">
<text/>
</element>
</optional>
<optional>
<element name="LENGTH">
<text/>
</element>
</optional>
<optional>
<element name="YEAR">
<text/>
</element>
</optional>
<oneOrMore>
<element name="ARTIST">
<text/>
</element>
</oneOrMore>
</element>
element SONG {
element TITLE { text },
element COMPOSER {
element NAME {
element GIVEN { text },
element FAMILY { text }
}
}+,
element PRODUCER {
element NAME {
element GIVEN { text },
element FAMILY { text }
}
}*,
element PUBLISHER { text }?,
element LENGTH { text }?,
element YEAR { text }?,
element ARTIST { text }+
}
PRODUCER
and COMPOSER
are
really the same type.
<?xml version="1.0"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0">
<start>
<element name="SONG">
<element name="TITLE">
<text/>
</element>
<oneOrMore>
<element name="COMPOSER">
<ref name="personContent"/>
</element>
</oneOrMore>
<zeroOrMore>
<element name="PRODUCER">
<ref name="personContent"/>
</element>
</zeroOrMore>
<optional>
<element name="PUBLISHER">
<text/>
</element>
</optional>
<optional>
<element name="LENGTH">
<text/>
</element>
</optional>
<optional>
<element name="YEAR">
<text/>
</element>
</optional>
<oneOrMore>
<element name="ARTIST">
<text/>
</element>
</oneOrMore>
</element>
</start>
<define name="personContent">
<element name="NAME">
<element name="GIVEN">
<text/>
</element>
<element name="FAMILY">
<text/>
</element>
</element>
</define>
</grammar>
start =
element SONG {
element TITLE { text },
element COMPOSER { personContent }+,
element PRODUCER { personContent }*,
element PUBLISHER { text }?,
element LENGTH { text }?,
element YEAR { text }?,
element ARTIST { text }+
}
personContent =
element NAME {
element GIVEN { text },
element FAMILY { text }
}
<?xml version="1.0"?>
<SONG>
<TITLE>Hot Cop</TITLE>
<ARTIST>Village People</ARTIST>
<YEAR>1978</YEAR>
<COMPOSER>
<NAME><FAMILY>Morali</FAMILY> <GIVEN>Jacques</GIVEN></NAME>
</COMPOSER>
<COMPOSER>
<NAME><GIVEN>Henri</GIVEN> <FAMILY>Belolo</FAMILY></NAME>
</COMPOSER>
<PRODUCER>
<NAME><GIVEN>Jacques</GIVEN> <FAMILY>Morali</FAMILY></NAME>
</PRODUCER>
<COMPOSER>
<NAME><FAMILY>Willis</FAMILY> <GIVEN>Victor</GIVEN></NAME>
</COMPOSER>
<PUBLISHER>PolyGram Records</PUBLISHER>
</SONG>
Much more powerful than either DTDs or schemas
<?xml version="1.0"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0">
<start>
<element name="SONG">
<element name="TITLE">
<text/>
</element>
<interleave>
<oneOrMore>
<element name="COMPOSER">
<ref name="personContent"/>
</element>
</oneOrMore>
<zeroOrMore>
<element name="PRODUCER">
<ref name="personContent"/>
</element>
</zeroOrMore>
<optional>
<element name="PUBLISHER">
<text/>
</element>
</optional>
<optional>
<element name="LENGTH">
<text/>
</element>
</optional>
<optional>
<element name="YEAR">
<text/>
</element>
</optional>
<oneOrMore>
<element name="ARTIST">
<text/>
</element>
</oneOrMore>
</interleave>
</element>
</start>
<define name="personContent">
<element name="NAME">
<interleave>
<element name="GIVEN">
<text/>
</element>
<element name="FAMILY">
<text/>
</element>
</interleave>
</element>
</define>
</grammar>
start =
element SONG {
element TITLE { text },
(element COMPOSER { personContent }+
& element PRODUCER { personContent }*
& element PUBLISHER { text }?
& element LENGTH { text }?
& element YEAR { text }?
& element ARTIST { text }+)
}
personContent =
element NAME {
element GIVEN { text }
& element FAMILY { text }
}
RELAX NG can enforce order and appearance of elements in mixed content.
Interleave with text
element to validate mixed content
<?xml version="1.0"?>
<SONG>
<TITLE>Hot Cop</TITLE>
<COMPOSER>
<NAME>Mr. <GIVEN>Jacques</GIVEN> <FAMILY>Morali</FAMILY> Esq.</NAME>
</COMPOSER>
<COMPOSER>
<NAME>Mr. <GIVEN>Henri</GIVEN> L. <FAMILY>Belolo</FAMILY>, M.D.</NAME>
</COMPOSER>
<COMPOSER>
<NAME>Mr. <GIVEN>Victor</GIVEN> C. <FAMILY>Willis</FAMILY></NAME>
</COMPOSER>
<PRODUCER>
<NAME>Mr. <GIVEN>Jacques</GIVEN> S. <FAMILY>Morali</FAMILY></NAME>
</PRODUCER>
<PUBLISHER>PolyGram Records</PUBLISHER>
<YEAR>1978</YEAR>
<ARTIST>Village People</ARTIST>
</SONG>
...
<define name="personContent">
<element name="NAME">
<interleave>
<text />
<element name="GIVEN">
<text/>
</element>
<element name="FAMILY">
<text/>
</element>
</interleave>
</element>
</define>
...
personContent =
element NAME {
text
& element GIVEN { text }
& element FAMILY { text }
}
choice
requires exactly one of a group
of specified items to appear
Can be enclosed in optional
, oneOrMore
, or zeroOrMore
A song must have at least one of ARTIST
, COMPOSER
, or PRODUCER
:
<?xml version="1.0"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0">
<start>
<element name="SONG">
<element name="TITLE">
<text/>
</element>
<oneOrMore>
<choice>
<element name="COMPOSER">
<ref name="personContent"/>
</element>
<element name="PRODUCER">
<ref name="personContent"/>
</element>
<element name="ARTIST">
<text/>
</element>
</choice>
</oneOrMore>
<optional>
<element name="PUBLISHER">
<text/>
</element>
</optional>
<optional>
<element name="LENGTH">
<text/>
</element>
</optional>
<optional>
<element name="YEAR">
<text/>
</element>
</optional>
</element>
</start>
<define name="personContent">
<element name="NAME">
<interleave>
<element name="GIVEN">
<text/>
</element>
<element name="FAMILY">
<text/>
</element>
</interleave>
</element>
</define>
</grammar>
start =
element SONG {
element TITLE { text },
(element COMPOSER { personContent }
| element PRODUCER { personContent }
| element ARTIST { text })+,
element PUBLISHER { text }?,
element LENGTH { text }?,
element YEAR { text }?
}
personContent =
element NAME {
element GIVEN { text }
& element FAMILY { text }
}
group
defines a model that can be used as a particle in other models
Like a sequence in W3C schemas or parentheses in DTDs
Allow a NAME
element to contain either plain text or a GIVEN
and a FAMILY
but not both:
<element name="NAME>
<choice>
<text/>
<group>
<interleave>
<element name="GIVEN">
<text/>
</element>
<element name="FAMILY">
<text/>
</element>
</interleave>
</group>
</choice>
</element>
<?xml version="1.0"?>
<SONG>
<TITLE>Hot Cop</TITLE>
<PHOTO ALT="Victor Willis in Cop Outfit" WIDTH="100" HEIGHT="200"/>
<COMPOSER>Jacques Morali</COMPOSER>
<COMPOSER>Henri Belolo</COMPOSER>
<COMPOSER>Victor Willis</COMPOSER>
<PRODUCER>Jacques Morali</PRODUCER>
<PUBLISHER>PolyGram Records</PUBLISHER>
<YEAR>1978</YEAR>
<ARTIST>Village People</ARTIST>
</SONG>
attribute
element
name
attribute specifies the name of the attribute
optional
element can make an attribute optional
<?xml version="1.0"?>
<element name="SONG" xmlns="http://relaxng.org/ns/structure/1.0">
<element name="TITLE">
<text/>
</element>
<element name="PHOTO">
<attribute name="ALT">
<text/>
</attribute>
<attribute name="WIDTH">
<text/>
</attribute>
<attribute name="HEIGHT">
<text/>
</attribute>
</element>
<oneOrMore>
<element name="COMPOSER">
<text/>
</element>
</oneOrMore>
<zeroOrMore>
<element name="PRODUCER">
<text/>
</element>
</zeroOrMore>
<optional>
<element name="PUBLISHER">
<text/>
</element>
</optional>
<optional>
<element name="LENGTH">
<text/>
</element>
</optional>
<optional>
<element name="YEAR">
<text/>
</element>
</optional>
<oneOrMore>
<element name="ARTIST">
<text/>
</element>
</oneOrMore>
</element>
element SONG {
element TITLE { text },
element PHOTO {
attribute ALT { text },
attribute WIDTH { text },
attribute HEIGHT { text }
},
element COMPOSER { text }+,
element PRODUCER { text }*,
element PUBLISHER { text }?,
element LENGTH { text }?,
element YEAR { text }?,
element ARTIST { text }+
}
List of all allowed values from which one must be chosen
A choice
containing value
elements
The publisher must be one of the oligopoly that controls 90% of U.S. music (Warner-Elektra-Atlantic, Universal Music Group, Sony Music Entertainment, Inc., Capitol Records, Inc., BMG Music)
<?xml version="1.0"?>
<element name="SONG" xmlns="http://relaxng.org/ns/structure/1.0">
<element name="TITLE">
<text/>
</element>
<oneOrMore>
<element name="COMPOSER">
<text/>
</element>
</oneOrMore>
<zeroOrMore>
<element name="PRODUCER">
<text/>
</element>
</zeroOrMore>
<optional>
<element name="PUBLISHER">
<choice>
<value>Warner-Elektra-Atlantic</value>
<value>Universal Music Group</value>
<value>Sony Music Entertainment, Inc.</value>
<value>Capitol Records, Inc.</value>
<value>BMG Music</value>
</choice>
</element>
</optional>
<optional>
<element name="LENGTH">
<text/>
</element>
</optional>
<optional>
<element name="YEAR">
<text/>
</element>
</optional>
<oneOrMore>
<element name="ARTIST">
<text/>
</element>
</oneOrMore>
</element>
element SONG {
element TITLE { text },
element COMPOSER { text }+,
element PRODUCER { text }*,
element PUBLISHER {
"Warner-Elektra-Atlantic"
| "Universal Music Group"
| "Sony Music Entertainment, Inc."
| "Capitol Records, Inc."
| "BMG Music"
}?,
element LENGTH { text }?,
element YEAR { text }?,
element ARTIST { text }+
}
ns
attribute specifies the namespace of the element it matches
Only applies in schema
Applies to that element
element and its descendants
Until overridden
Validates solely based on URI; irrelevant whether instance document uses prefixes or not
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<SONG xmlns="http://www.cafeconleche.org/namespace/song">
<TITLE>Hot Cop</TITLE>
<PHOTO ALT="Victor Willis in Cop Outfit" WIDTH="100" HEIGHT="200"/>
<COMPOSER>Jacques Morali</COMPOSER>
<COMPOSER>Henri Belolo</COMPOSER>
<COMPOSER>Victor Willis</COMPOSER>
<PRODUCER>Jacques Morali</PRODUCER>
<PUBLISHER>PolyGram Records</PUBLISHER>
<YEAR>1978</YEAR>
<ARTIST>Village People</ARTIST>
</SONG>
<?xml version="1.0"?>
<element xmlns="http://relaxng.org/ns/structure/1.0"
name="SONG"
ns="http://www.cafeconleche.org/namespace/song">
<element name="TITLE">
<text/>
</element>
<element name="PHOTO">
<attribute name="ALT">
<text/>
</attribute>
<attribute name="WIDTH">
<text/>
</attribute>
<attribute name="HEIGHT">
<text/>
</attribute>
</element>
<oneOrMore>
<element name="COMPOSER">
<text/>
</element>
</oneOrMore>
<zeroOrMore>
<element name="PRODUCER">
<text/>
</element>
</zeroOrMore>
<optional>
<element name="PUBLISHER">
<text/>
</element>
</optional>
<optional>
<element name="LENGTH">
<text/>
</element>
</optional>
<optional>
<element name="YEAR">
<text/>
</element>
</optional>
<oneOrMore>
<element name="ARTIST">
<text/>
</element>
</oneOrMore>
</element>
default namespace = "http://www.cafeconleche.org/namespace/song"
element SONG {
element TITLE { text },
element PHOTO {
attribute ALT { text },
attribute WIDTH { text },
attribute HEIGHT { text }
},
element COMPOSER { text }+,
element PRODUCER { text }*,
element PUBLISHER { text }?,
element LENGTH { text }?,
element YEAR { text }?,
element ARTIST { text }+
}
Can use prefixed names instead if you prefer
Usual namespace prefix binding attributes
Convenient in rare cases when namespaces shift from one element to the next (two widely intermixed vocabularies like XSLT)
<?xml version="1.0"?>
<element xmlns="http://relaxng.org/ns/structure/1.0"
name="sng:SONG"
xmlns:sng="http://www.cafeconleche.org/namespace/song">
<element name="sng:TITLE">
<text/>
</element>
<element name="sng:PHOTO">
<attribute name="ALT">
<text/>
</attribute>
<attribute name="WIDTH">
<text/>
</attribute>
<attribute name="HEIGHT">
<text/>
</attribute>
</element>
<oneOrMore>
<element name="sng:COMPOSER">
<text/>
</element>
</oneOrMore>
<zeroOrMore>
<element name="sng:PRODUCER">
<text/>
</element>
</zeroOrMore>
<optional>
<element name="sng:PUBLISHER">
<text/>
</element>
</optional>
<optional>
<element name="sng:LENGTH">
<text/>
</element>
</optional>
<optional>
<element name="sng:YEAR">
<text/>
</element>
</optional>
<oneOrMore>
<element name="sng:ARTIST">
<text/>
</element>
</oneOrMore>
</element>
namespace sng = "http://www.cafeconleche.org/namespace/song"
element sng:SONG {
element sng:TITLE { text },
element sng:PHOTO {
attribute ALT { text },
attribute WIDTH { text },
attribute HEIGHT { text }
},
element sng:COMPOSER { text }+,
element sng:PRODUCER { text }*,
element sng:PUBLISHER { text }?,
element sng:LENGTH { text }?,
element sng:YEAR { text }?,
element sng:ARTIST { text }+
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<SONG xmlns="http://www.cafeconleche.org/namespace/song"
xmlns:xlink="http://www.w3.org/1999/xlink">
<TITLE>Hot Cop</TITLE>
<PHOTO xlink:type="simple" xlink:href="hotcop.jpg"
xlink:actuate="onLoad" xlink:show="embed"
ALT="Victor Willis in Cop Outfit" WIDTH="100" HEIGHT="200"/>
<COMPOSER>Jacques Morali</COMPOSER>
<COMPOSER>Henri Belolo</COMPOSER>
<COMPOSER>Victor Willis</COMPOSER>
<PRODUCER>Jacques Morali</PRODUCER>
<PUBLISHER>PolyGram Records</PUBLISHER>
<YEAR>1978</YEAR>
<ARTIST>Village People</ARTIST>
</SONG>
attribute
element can have an ns
attribute to specify the namespace
attribute
element does not inherit ns
attribute
<?xml version="1.0"?>
<element xmlns="http://relaxng.org/ns/structure/1.0"
name="SONG"
ns="http://www.cafeconleche.org/namespace/song">
<element name="TITLE">
<text/>
</element>
<element name="PHOTO">
<attribute name="ALT">
<text/>
</attribute>
<attribute name="WIDTH">
<text/>
</attribute>
<attribute name="HEIGHT">
<text/>
</attribute>
<attribute name="type" ns="http://www.w3.org/1999/xlink">
<text/>
</attribute>
<attribute name="href" ns="http://www.w3.org/1999/xlink">
<text/>
</attribute>
<attribute name="actuate" ns="http://www.w3.org/1999/xlink">
<text/>
</attribute>
<attribute name="show" ns="http://www.w3.org/1999/xlink">
<text/>
</attribute>
</element>
<oneOrMore>
<element name="COMPOSER">
<text/>
</element>
</oneOrMore>
<zeroOrMore>
<element name="PRODUCER">
<text/>
</element>
</zeroOrMore>
<optional>
<element name="PUBLISHER">
<text/>
</element>
</optional>
<optional>
<element name="LENGTH">
<text/>
</element>
</optional>
<optional>
<element name="YEAR">
<text/>
</element>
</optional>
<oneOrMore>
<element name="ARTIST">
<text/>
</element>
</oneOrMore>
</element>
default namespace = "http://www.cafeconleche.org/namespace/song"
namespace ns1 = "http://www.w3.org/1999/xlink"
element SONG {
element TITLE { text },
element PHOTO {
attribute ALT { text },
attribute WIDTH { text },
attribute HEIGHT { text },
attribute ns1:type { text },
attribute ns1:href { text },
attribute ns1:actuate { text },
attribute ns1:show { text }
},
element COMPOSER { text }+,
element PRODUCER { text }*,
element PUBLISHER { text }?,
element LENGTH { text }?,
element YEAR { text }?,
element ARTIST { text }+
}
Consider this document:
<foo>
<value>45.67</value>
</foo>
What is the type of value
?
A longitude or latitude
A decimal monetary type, as in COBOL
A fixed point number
An infinitely precise floating point number such as
represented by the java.math.BigDecimal
class
An IEEE754 double
A Java double
An IEEE 754 float
A VAX Fortran REAL
An imprecisely known decimal number with 4 significant digits that's plus or minus 1 in the last place.
An imprecisely known decimal number with 4 significant digits that's plus or minus 5 in the last place.
Build 67 of version 45 of Microsoft Word
A regular expression matching all strings that begin with the two characters '4' and '5', followed by a single character, followed by the two characters '6' and '7'.
A string of characters a monkey typed on a keyboard
Other interpretations are doubtless possible, and even make sense in particular contexts.
There's no guarantee that the string 45.67
in fact represents any particular type.
RELAX NG defines no data types beyond text
But implementations are free to implement other type libraries
W3C XML Schema Data Types are commonly available
User defined type libraries
data
element replaces text
element in content models
<?xml version="1.0"?>
<element name="SONG" xmlns="http://relaxng.org/ns/structure/1.0"
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<element name="TITLE">
<text/>
</element>
<interleave>
<oneOrMore>
<element name="COMPOSER">
<data type="string"/>
</element>
</oneOrMore>
<zeroOrMore>
<element name="PRODUCER">
<data type="string"/>
</element>
</zeroOrMore>
<oneOrMore>
<element name="ARTIST">
<data type="string"/>
</element>
</oneOrMore>
<optional>
<element name="PUBLISHER">
<data type="string"/>
</element>
</optional>
<optional>
<element name="LENGTH">
<data type="string"/>
</element>
</optional>
<optional>
<element name="YEAR">
<data type="gYear" />
</element>
</optional>
<element name="PRICE">
<data type="string">
<param name="pattern">\p{Sc}\p{Nd}+(\.\p{Nd}\p{Nd})?</param>
</data>
</element>
</interleave>
</element>
namespace ns1 = "http://www.w3.org/1999/xhtml"
element SONG {
element TITLE { text },
(element COMPOSER { xsd:string }+
& element PRODUCER { xsd:string }*
& element ARTIST { xsd:string }+
& element PUBLISHER { xsd:string }?
& element LENGTH { xsd:string }?
& element YEAR { xsd:gYear }?
& element PRICE {
xsd:string { pattern = "\p{Sc}\p{Nd}+(\.\p{Nd}\p{Nd})?" }
})
}
Boolean
String
URIs
Numeric types
Time types
XML types
XML Schema Built-In Numeric Simple Types | ||
---|---|---|
Name | Type | Examples |
float | IEEE 754 32-bit floating point number | -INF, -1E4, -0, 0, 12.78E-2, 12, INF, NaN |
double | IEEE 754 64-bit floating point number | -INF, 1.401E-90, -1E4, -0, 0, 12.78E-2, 12, INF, NaN, 3.4E42 |
decimal | arbitrary precision, decimal numbers | -2.7E400, 5.7E-444, -3.1415292, 0, 7.8, 90200.76, 3.4E1024 |
integer | an arbitrarily large or small integer | -500000000000000000000000, -9223372036854775809, -126789, -1, 0, 1, 5, 23, 42, 126789, 9223372036854775808, 456734987324983264987362495809587095720978 |
nonPositiveInteger | an integer less than or equal to zero | 0, -1, -2, -3, -4, -5, ... |
negativeInteger | an integer strictly less than zero | -1, -2, -3, -4, -5, ... |
long | an eight-byte two's complement integer such as Java's
long type |
-9223372036854775808, -12678967543233, -1, 9223372036854775807 |
int | an integer that can be represented as a four-byte,
two's complement number such as Java's int type |
-2147483648, -1, 0, 1, 5, 23, 42, 2147483647 |
short | an integer that can be represented as a two-byte,
two's complement number such as Java's short type |
-32768, -1, 0, 1, 5, 23, 42, 32767 |
byte | an integer that can be represented as a one-byte,
two's complement number such as Java's byte type |
-128, -1, 0, 1, 5, 23, 42, 127 |
nonNegativeInteger | an integer greater than or equal to zero | 0, 1, 2, 3, 4, 5, ... |
unsignedLong | an eight-byte unsigned integer | 0, 1, 2, 3, 4, 5, ...18446744073709551614, 18446744073709551615 |
unsignedInt | a four-byte unsigned integer | 0, 1, 2, 3, 4, 5, ...4294967294, 4294967295 |
unsignedShort | a two-byte unsigned integer | 0, 1, 2, 3, 4, 5, ...65534, 65535 |
unsignedByte | a one-byte unsigned integer | 0, 1, 2, 3, 4, 5, ...254, 255 |
positiveInteger | an integer strictly greater than zero | 1, 2, 3, 4, 5, 6, ... |
XML Schema Built-In Time Simple Types | ||
---|---|---|
Name | Type | Examples |
dateTime | a particular moment in Coordinated Universal Time; up to an arbitrarily small fraction of a second | 1999-05-31T13:20:00.000-05:00 |
gMonth | A given month in a given year | 2000-10 |
gYear | a given year | 2000 |
gMonthDay | a date in no particular year, or rather in every year | --10-31 |
gDay | a day in no particular month, or rather in every month | ----31 |
duration | a length of time, without fixed endpoints, to an arbitrary fraction of a second | P2000Y10M31DT09H32M7.4312S |
date | a specific day in history | 2000-10-31 |
time | a specific time of day, that recurs every day | 14:30:00.000, 09:30:00.000-05:00 |
XML Schema Built-In XML Simple Types | ||
---|---|---|
Name | Type | Examples |
ID | XML 1.0 ID attribute type | any XML name that's unique among ID type attributes |
IDREF | XML 1.0 IDREF attribute type | any XML name that's used as an ID type attribute elsewhere in the document |
ENTITY | XML 1.0 ENTITY attribute type | any XML name that's declared as an unparsed entity in the DTD |
NOTATION | ???? | ???? |
language | Permissible values for xml:lang as defined in XML 1.0
|
en-GB, en-US, fr |
IDREFS | XML 1.0 IDREFS attribute type | a white space separated list of IDREF names |
ENTITIES | XML 1.0 ENTITIES attribute type | a white space separated list of ENTITY names |
NMTOKEN | XML 1.0 NMTOKEN attribute type | 12 are you ready |
NMTOKENS | XML 1.0 NMTOKENS attribute type | a white space separated list of name tokens |
Name | An XML 1.0 Name | set, title, rdf, math, math123, href |
QName | an optionally prefixed, namespace qualified name | song:title |
NCName | a local name without any colons | title |
XML Schema Built-In Simple Types | ||
---|---|---|
Name | Type | Examples |
string | Parsed Character Data; #PCDATA | Hot Cop |
normalizedString | A string whose normalized value does not contain any tabs, carriage returns, or linefeeds | PIC1, PIC2, PIC3, cow_movie, MonaLisa, Hello World , Warhol, red green |
token | A string whose normalized value has no leading or trailing white space, no tabs, no linefeeds, and not more than one consecutive space | p1 p2, ss123 45 6789, _92, red, green, NT Decl, seventeenp1, p2, 123 45 6789, ^*&^*&_92, red green blue, NT-Decl, seventeen; Mary had a little lamb, The love of money is the root of all Evil. |
boolean | C++'s bool type |
true, false, 1, 0 |
anyURI | relative or absolute URI | http://www.w3.org/TR/2000/WD-xmlschema-2-20000407/#duration, /javafaq/reports/JCE1.2.1.html |
hexBinary | Arbitrary binary data encoded in hexadecimal form | A4E345EC54CC8D52198000FFEA6C |
base64Binary | Arbitrary binary data encoded in Base64 | 6jKpNnmkkWeArsn5Oeeg2njcz+nXdk0f9kZI892ddlR8Lg1aMhPeFTYuoq3I6neFlb BjWzuktNZKiXYBfKsSTB8U09dTiJo2ir3HJuY7eW/p89osKMfixPQsp9vQMgzph6Qa lY7j4MB7y5ROJYsTr1/fFwmj/yhkHwpbpzed1LE= |
param
children of data
specify the constraints on the type to create a subset of the normally accepted values
For example, this data
element restricts a date to be any
year from 1877 (the year Edison invented the
phonograph) on:
<element name="YEAR">
<data type="gYear">
<param name="minInclusive">1877</param/>
</data>
</element>
In the compact syntax:
element YEAR {xsd:gYear {minInclusive = "1877"}}
Facets include:
length
minLength
maxLength
pattern
maxInclusive
maxExclusive
minInclusive
minExclusive
totalDigits
fractionDigits
Not all facets apply to all types.
Facets not allowed/applicable in RELAX NG:
enumeration
whiteSpace
The number of units allowed in a value
For strings (string
,
normalizedString
, token
,
QName
, NCname
,
ID
, IDREF
,
language
, anyURI
, ENTITY
,
NOTATION
, and NMTOKEN
)
the units are characters
For lists (IDREFS
, ENTITIES
,
and
NMTOKENS
) the units are tokens
For binary types (hexBinary
, base64Binary
)
the units are bytes after decoding
Must be a non-negative integer
For example, to say that all names and titles must contain between 1 and 255 characters:
<?xml version="1.0"?>
<element name="SONG" xmlns="http://relaxng.org/ns/structure/1.0"
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<element name="TITLE">
<data type="string">
<param name="minLength">1</param>
<param name="maxLength">255</param>
</data>
</element>
<oneOrMore>
<element name="COMPOSER">
<data type="string">
<param name="minLength">1</param>
<param name="maxLength">255</param>
</data>
</element>
</oneOrMore>
<zeroOrMore>
<element name="PRODUCER">
<data type="string">
<param name="minLength">1</param>
<param name="maxLength">255</param>
</data>
</element>
</zeroOrMore>
<optional>
<element name="PUBLISHER">
<data type="string">
<param name="minLength">1</param>
<param name="maxLength">255</param>
</data>
</element>
</optional>
<optional>
<element name="LENGTH">
<data type="string"/>
</element>
</optional>
<optional>
<element name="YEAR">
<data type="gYear"/>
</element>
</optional>
<oneOrMore>
<element name="ARTIST">
<data type="string"/>
</element>
</oneOrMore>
</element>
In compact syntax:
element SONG {
element TITLE {
xsd:string { minLength = "1" maxLength = "255" }
},
element COMPOSER {
xsd:string { minLength = "1" maxLength = "255" }
}+,
element PRODUCER {
xsd:string { minLength = "1" maxLength = "255" }
}*,
element PUBLISHER {
xsd:string { minLength = "1" maxLength = "255" }
}?,
element LENGTH { xsd:string }?,
element YEAR { xsd:gYear }?,
element ARTIST { xsd:string }+
}
Determines the minimum and maximum allowed values
Applies to ordered simple types including
byte
, unsignedByte
,
integer
, positiveInteger
,
negativeInteger
, nonNegativeInteger
,
nonPositiveInteger
, int
,
unsignedInt
, long
,
number
, unsignedLong
,
short
, unsignedShort
, number
,
float
, double
, time
,
dateTime
,
duration
, date
, gMonth
,
gYear
, gDay
,
and gMonthDay
.
For example, to say that the year must be between 1877 and 2100:
<?xml version="1.0"?>
<element name="SONG" xmlns="http://relaxng.org/ns/structure/1.0"
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<element name="TITLE">
<text/>
</element>
<oneOrMore>
<element name="COMPOSER">
<data type="string"/>
</element>
</oneOrMore>
<zeroOrMore>
<element name="PRODUCER">
<data type="string"/>
</element>
</zeroOrMore>
<optional>
<element name="PUBLISHER">
<data type="string"/>
</element>
</optional>
<optional>
<element name="LENGTH">
<data type="string"/>
</element>
</optional>
<optional>
<element name="YEAR">
<data type="gYear">
<param name="minInclusive">1877</param>
<param name="maxInclusive">2100</param>
</data>
</element>
</optional>
<oneOrMore>
<element name="ARTIST">
<data type="string"/>
</element>
</oneOrMore>
</element>
In the compact syntax:
element SONG {
element TITLE { text },
element COMPOSER { xsd:string }+,
element PRODUCER { xsd:string }*,
element PUBLISHER { xsd:string }?,
element LENGTH { xsd:string }?,
element YEAR {
xsd:gYear { minInclusive = "1877"
maxInclusive = "2100"
}
}?,
element ARTIST { xsd:string }+
}
totalDigits
facet
specifies the maximum number of decimal digits in a number
as a positive integer
fractionDigits
facet
specifies the maximum number of decimal digits to the right of the decimal
point as a non-negative integer
Applies to all types derived from decimal
including byte
, unsignedByte
,
integer
, positiveInteger
,
negativeInteger
, nonNegativeInteger
,
nonPositiveInteger
,
int
, unsignedInt
, long
,
unsignedLong
, short
, and
unsignedShort
.
Does not apply to float
and double
You can specify at most two fractional digits or at most seven decimal digits, but not at least two fractional digits or exactly seven decimal digits
<?xml version="1.0"?>
<SONG>
<TITLE>Hot Cop</TITLE>
<COMPOSER>Jacques Morali</COMPOSER>
<COMPOSER>Henri Belolo</COMPOSER>
<COMPOSER>Victor Willis</COMPOSER>
<PRODUCER>Jacques Morali</PRODUCER>
<PUBLISHER>PolyGram Records</PUBLISHER>
<YEAR>1978</YEAR>
<ARTIST>Village People</ARTIST>
<PRICE>$1.35</PRICE>
</SONG>
Suppose you want a money type to specify that the PRICE
element content must look like $1.35 or ¥11000
Use the pattern
facet to specify
a regular expression instances must match
More or less Perl-like including the Unicode extensions introduced in Perl 5.6
The money regular expression:
\p{Sc}\p{Nd}+(\.\p{Nd}\p{Nd})?
\p{Sc}
\p{Nd}
\p{Nd}+
\.
(\.\p{Nd}\p{Nd})
(\.\p{Nd}\p{Nd})?
<?xml version="1.0"?>
<element name="SONG" xmlns="http://relaxng.org/ns/structure/1.0"
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<element name="TITLE">
<text/>
</element>
<oneOrMore>
<element name="COMPOSER">
<data type="string"/>
</element>
</oneOrMore>
<zeroOrMore>
<element name="PRODUCER">
<data type="string"/>
</element>
</zeroOrMore>
<optional>
<element name="PUBLISHER">
<data type="string"/>
</element>
</optional>
<optional>
<element name="LENGTH">
<data type="string"/>
</element>
</optional>
<optional>
<element name="YEAR">
<data type="gYear" />
</element>
</optional>
<oneOrMore>
<element name="ARTIST">
<data type="string"/>
</element>
</oneOrMore>
<element name="PRICE">
<data type="string">
<param name="pattern">\p{Sc}\p{Nd}+(\.\p{Nd}\p{Nd})?</param>
</data>
</element>
</element>
element SONG {
element TITLE { text },
element COMPOSER { xsd:string }+,
element PRODUCER { xsd:string }*,
element PUBLISHER { xsd:string }?,
element LENGTH { xsd:string }?,
element YEAR { xsd:gYear }?,
element ARTIST { xsd:string }+,
element PRICE {
xsd:string { pattern = "\p{Sc}\p{Nd}+(\.\p{Nd}\p{Nd})?" }
}
}
Matches a list of words separated by white space
Each word has a type
Can have multiple types
Allow multiple years in the YEAR
element:
<element name="YEAR">
<list>
<oneOrMore>
<data type="gYear"/>
</oneOrMore>
</list>
</element>
element SONG {
element TITLE { text },
element COMPOSER { xsd:string }+,
element PRODUCER { xsd:string }*,
element PUBLISHER { xsd:string }?,
element LENGTH { xsd:string }?,
element YEAR {
list { xsd:gYear+ }
}?,
element ARTIST { xsd:string }+
}
Available in Java (1.2 or later) and .NET
Implement the org.relaxng.datatype.DatatypeLibraryFactory
interface
package com.elharo.xml.relaxng;
import org.relaxng.datatype.*;
import org.relaxng.datatype.helpers.StreamingValidatorImpl;
public class PrimeDatatype implements Datatype {
private boolean isPrime(String literal) {
try {
int candidate = Integer.parseInt(literal);
if (candidate < 2) return false;
double max = Math.sqrt(candidate);
for (int i = 2; i <= max; i++) {
if (candidate % i == 0) return false;
}
return true;
}
catch (NumberFormatException ex) {
return false;
}
}
public boolean isValid(String literal, ValidationContext context) {
return isPrime(literal);
}
public void checkValid(String literal, ValidationContext context)
throws DatatypeException {
if (!isValid(literal, context)) {
throw new DatatypeException(literal + " is not a prime number");
}
}
public DatatypeStreamingValidator createStreamingValidator(ValidationContext context) {
// Inefficient but simple. StreamingValidatorImpl stores
// everything in a string and then validates the string. More
// efficient implementations can validate piece by piece.
return new StreamingValidatorImpl(this, context);
}
// These next three methods are closely tied together. The object
// returned by this method si only used for sameValue and hashCode.
public Object createValue(String literal, ValidationContext context) {
if (isPrime(literal)) {
return Integer.valueOf(literal);
}
return null;
}
public boolean sameValue(Object value1, Object value2) {
if (value1 == null) return value2 == null;
else return value1.equals(value2);
}
public int valueHashCode(Object value) {
return value.hashCode();
}
public int getIdType() {
return ID_TYPE_NULL;
}
public boolean isContextDependent() {
return false;
}
}
package com.elharo.xml.relaxng;
import org.relaxng.datatype.*;
import org.relaxng.datatype.helpers.*;
public class PrimeDatatypeLibrary implements DatatypeLibrary {
public Datatype createDatatype(String typeLocalName)
throws DatatypeException {
if ("prime".equals(typeLocalName)) {
return new PrimeDatatype();
}
throw new DatatypeException("Unsupported type: " + typeLocalName);
}
public DatatypeBuilder createDatatypeBuilder(String baseTypeLocalName)
throws DatatypeException {
return new ParameterlessDatatypeBuilder(
createDatatype("prime")
);
}
}
package com.elharo.xml.relaxng;
import org.relaxng.datatype.*;
public class PrimeDatatypeLibraryFactory implements DatatypeLibraryFactory {
public final static String namespace = "http://ns.cafeconleche.org/relaxng/primes";
public DatatypeLibrary createDatatypeLibrary(String namespace) {
if (PrimeDatatypeLibraryFactory.namespace.equals(namespace)) {
return new PrimeDatatypeLibrary();
}
return null;
}
}
Put the datatype library in a JAR archive
Include a
META-INF/services/org.relaxng.datatype.DatatypeLibraryFactory
file that contains the name of the class that implements the
org.relaxng.datatype.DatatypeLibraryFactory
interface
Add the .jar file to the CLASSPATH
Add the MSV/Jing .jar file to the CLASSPATH (Runnable archives don't work)
<?xml version="1.0"?>
<numbers>
<number>2</number>
<number>3</number>
<number>4</number>
<number>5</number>
<number>6</number>
</numbers>
<?xml version="1.0"?>
<element name="numbers" xmlns="http://relaxng.org/ns/structure/1.0">
<oneOrMore>
<element name="number">
<data type="prime"
datatypeLibrary="http://ns.cafeconleche.org/relaxng/primes"/>
</element>
</oneOrMore>
</element>
datatypes d = "http://ns.cafeconleche.org/relaxng/primes"
element numbers {
element number { d:prime }+
}
Breaks schema into multiple parts.
<!-- ==============================================================
SVG12-RNG | SVG 1.2 Full Driver
============================================================== -->
<grammar ns='http://www.w3.org/2000/svg' xml:lang='en'
xmlns='http://relaxng.org/ns/structure/1.0'
xmlns:xlink='http://www.w3.org/1999/xlink'
xmlns:a='http://relaxng.org/ns/compatibility/annotations/1.0'
xmlns:svg='http://www.w3.org/2000/svg'
datatypeLibrary='http://www.w3.org/2001/XMLSchema-datatypes'>
<!-- ... common libraries and attributes ........................ -->
<include href='../Tiny-1.2/datatypes.rng'/>
<include href='../Tiny-1.2/core-attrib.rng'/>
<include href='../Tiny-1.2/tiny-paint-attrib.rng'/>
<include href='../Tiny-1.2/graphics-attrib.rng'/>
<include href='../Tiny-1.2/transform-attrib.rng'/>
<include href='../Tiny-1.2/xlink-attrib.rng'/>
<include href='../Tiny-1.2/extresources-attrib.rng'/>
<include href='../Tiny-1.2/media-attrib.rng'/>
<include href='../Tiny-1.2/focusable-attrib.rng'/>
<include href='../Tiny-1.2/editable-attrib.rng'/>
<include href='../Tiny-1.2/backgroundfill-attrib.rng'/>
<include href='opacity-attrib.rng'/>
<include href='vectoreffects-attrib.rng'/>
<include href='viewport-attrib.rng'/>
<!-- ... element definitions .................................... -->
<include href='structure.rng'/>
<include href='../Tiny-1.2/conditional.rng'/>
<include href='../Tiny-1.2/image.rng'/>
<include href='../Tiny-1.2/shape.rng'/>
<include href='text.rng'/>
<include href='../Tiny-1.2/hyperlink.rng'/>
<include href='../Tiny-1.2/script.rng'/>
<include href='../Tiny-1.2/animation.rng'/>
<include href='font.rng'/>
<include href='../Tiny-1.2/extensibility.rng'/>
<include href='gradient.rng'/>
<include href='../Tiny-1.2/solidcolor.rng'/>
<include href='../Tiny-1.2/audio.rng'/>
<include href='../Tiny-1.2/video.rng'/>
<include href='../Tiny-1.2/animation-element.rng'/>
<include href='../Tiny-1.2/page.rng'/>
<include href='flow.rng'/>
<include href='../Tiny-1.2/handler.rng'/>
<include href='../Tiny-1.2/prefetch.rng'/>
<!-- ... only available in Full ................................. -->
<include href='animevents-attrib.rng'/>
<include href='docevents-attrib.rng'/>
<include href='graphevents-attrib.rng'/>
<include href='clip.rng'/>
<include href='filter.rng'/>
<include href='mask.rng'/>
<include href='pattern.rng'/>
<include href='style.rng'/>
<include href='transition.rng'/>
<include href='vectoreffects.rng'/>
<include href='extendedlink.rng'/>
<include href='cursor.rng'/>
<include href='compositing-attrib.rng'/>
<include href='marker.rng'/>
<include href='profile.rng'/>
<include href='shadow-attrib.rng'/>
<include href='tooltip-attrib.rng'/>
<include href='view.rng'/>
<include href='xmlevents.rng'/>
<include href='multiImage.rng'/>
<include href='deviceColor.rng'/>
<include href='traitDef.rng'/>
</grammar>
namespace a = "http://relaxng.org/ns/compatibility/annotations/1.0"
namespace svg = "http://www.w3.org/2000/svg"
namespace xlink = "http://www.w3.org/1999/xlink"
# ==============================================================
# SVG12-RNG | SVG 1.2 Full Driver
# ==============================================================
[ xml:lang = "en" ]
grammar {
# ... common libraries and attributes ........................
include "datatypes.rnc" inherit = svg
include "core-attrib.rnc" inherit = svg
include "tiny-paint-attrib.rnc" inherit = svg
include "graphics-attrib.rnc" inherit = svg
include "transform-attrib.rnc" inherit = svg
include "xlink-attrib.rnc" inherit = svg
include "extresources-attrib.rnc" inherit = svg
include "media-attrib.rnc" inherit = svg
include "focusable-attrib.rnc" inherit = svg
include "editable-attrib.rnc" inherit = svg
include "backgroundfill-attrib.rnc" inherit = svg
include "opacity-attrib.rnc" inherit = svg
include "vectoreffects-attrib.rnc" inherit = svg
include "viewport-attrib.rnc" inherit = svg
# ... element definitions ....................................
include "structure.rnc" inherit = svg
include "conditional.rnc" inherit = svg
include "image.rnc" inherit = svg
include "shape.rnc" inherit = svg
include "text.rnc" inherit = svg
include "hyperlink.rnc" inherit = svg
include "script.rnc" inherit = svg
include "animation.rnc" inherit = svg
include "font.rnc" inherit = svg
include "extensibility.rnc" inherit = svg
include "gradient.rnc" inherit = svg
include "solidcolor.rnc" inherit = svg
include "audio.rnc" inherit = svg
include "video.rnc" inherit = svg
include "animation-element.rnc" inherit = svg
include "page.rnc" inherit = svg
include "flow.rnc" inherit = svg
include "handler.rnc" inherit = svg
include "prefetch.rnc" inherit = svg
# ... only available in Full .................................
include "animevents-attrib.rnc" inherit = svg
include "docevents-attrib.rnc" inherit = svg
include "graphevents-attrib.rnc" inherit = svg
include "clip.rnc" inherit = svg
include "filter.rnc" inherit = svg
include "mask.rnc" inherit = svg
include "pattern.rnc" inherit = svg
include "style.rnc" inherit = svg
include "transition.rnc" inherit = svg
include "vectoreffects.rnc" inherit = svg
include "extendedlink.rnc" inherit = svg
include "cursor.rnc" inherit = svg
include "compositing-attrib.rnc" inherit = svg
include "marker.rnc" inherit = svg
include "profile.rnc" inherit = svg
include "shadow-attrib.rnc" inherit = svg
include "tooltip-attrib.rnc" inherit = svg
include "view.rnc" inherit = svg
include "xmlevents.rnc" inherit = svg
include "multiImage.rnc" inherit = svg
include "deviceColor.rnc" inherit = svg
include "traitDef.rnc" inherit = svg
}
Matches any pattern defined in an external schema
href
attribute points to the external schema
<?xml version="1.0"?>
<SONG>
<TITLE>Hot Cop</TITLE>
<COMPOSER>Jacques Morali</COMPOSER>
<COMPOSER>Henri Belolo</COMPOSER>
<COMPOSER>Victor Willis</COMPOSER>
<PRODUCER>Jacques Morali</PRODUCER>
<PUBLISHER>PolyGram Records</PUBLISHER>
<LENGTH>6:20</LENGTH>
<YEAR>1978</YEAR>
<ARTIST>Village People</ARTIST>
<DESCRIPTION>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>The schema requires this</title>
</head>
<body>
<p>
A catchy little ditty that never achieved the
success of <cite>YMCA</cite> or <cite>In the Navy</cite>.
</p>
</body>
</html>
</DESCRIPTION>
</SONG>
<?xml version="1.0"?>
<element name="SONG" xmlns="http://relaxng.org/ns/structure/1.0">
<element name="TITLE">
<text/>
</element>
<oneOrMore>
<element name="COMPOSER">
<text/>
</element>
</oneOrMore>
<zeroOrMore>
<element name="PRODUCER">
<text/>
</element>
</zeroOrMore>
<optional>
<element name="PUBLISHER">
<text/>
</element>
</optional>
<optional>
<element name="LENGTH">
<text/>
</element>
</optional>
<optional>
<element name="YEAR">
<text/>
</element>
</optional>
<oneOrMore>
<element name="ARTIST">
<text/>
</element>
</oneOrMore>
<optional>
<element name="DESCRIPTION">
<externalRef href="http://thaiopensource.com/relaxng/xhtml/xhtml-basic.rng"/>
</element>
</optional>
</element>
element SONG {
element TITLE { text },
element COMPOSER { text }+,
element PRODUCER { text }*,
element PUBLISHER { text }?,
element LENGTH { text }?,
element YEAR { text }?,
element ARTIST { text }+,
element DESCRIPTION { external "xhtml-basic.rnc" }?
}
All elements/attributes from namespaces other than http://relaxng.org/ns/structure/1.0 are ignored
Annotate with XHTML, Schematron or any other namespaced vocabulary
Use RELAX NG div
element to group multiple elements in a grammar
together with a single annotation
<?xml version="1.0"?>
<element name="SONG" xmlns="http://relaxng.org/ns/structure/1.0"
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<element name="TITLE">
<text/>
</element>
<interleave>
<oneOrMore>
<element name="COMPOSER">
<data type="string"/>
</element>
</oneOrMore>
<zeroOrMore>
<element name="PRODUCER">
<data type="string"/>
</element>
</zeroOrMore>
<oneOrMore>
<element name="ARTIST">
<data type="string"/>
</element>
</oneOrMore>
<optional>
<element name="PUBLISHER">
<data type="string"/>
</element>
</optional>
<optional>
<element name="LENGTH">
<data type="string"/>
</element>
</optional>
<optional>
<element name="YEAR">
<data type="gYear" />
</element>
</optional>
<element name="PRICE">
<data type="string">
<param name="pattern">\p{Sc}\p{Nd}+(\.\p{Nd}\p{Nd})?</param>
</data>
</element>
</interleave>
</element>
namespace ns1 = "http://www.w3.org/1999/xhtml"
element SONG {
element TITLE { text },
(element COMPOSER { xsd:string }+
& element PRODUCER { xsd:string }*
& element ARTIST { xsd:string }+
& element PUBLISHER { xsd:string }?
& element LENGTH { xsd:string }?
& element YEAR { xsd:gYear }?
& element PRICE {
xsd:string { pattern = "\p{Sc}\p{Nd}+(\.\p{Nd}\p{Nd})?" }
})
}
Not bundled; must install third party library
import javax.xml.*;
import javax.xml.validation.*;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import org.xml.sax.*;
public class Relax implements ErrorHandler {
public static void main(String[] args) {
if (args.length < 2) {
System.out.println("Usage: java Relax schema.rng file.xml");
return;
}
Schema schema;
try {
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.RELAXNG_NS_URI);
schema = factory.newSchema(new File(args[1]));
}
catch (SAXException ex) {
System.out.println("Schema error in " + args[0]);
System.out.println(ex.getMessage());
return;
}
catch (IllegalArgumentException ex) {
System.out.println("This implementation does not support RELAX NG.");
return;
}
Validator validator = schema.newValidator();
Source source = new StreamSource(new File(args[1]));
validator.setErrorHandler(new Relax());
try {
validator.validate(source);
}
catch (SAXException ex) {
System.out.println(args[1] + " is not well-formed.");
System.out.println(ex.getMessage());
}
catch (IOException ex) {
System.out.println("Could not validate " + args[1] + " due to an I/O error.");
System.out.println(ex.getMessage());
}
}
public void warning(SAXParseException exception) {
System.out.println("Warning: " + exception.getMessage());
System.out.println(" at line " + exception.getLineNumber()
+ ", column " + exception.getColumnNumber());
}
public void error(SAXParseException exception) {
System.out.println("Error: " + exception.getMessage());
System.out.println(" at line " + exception.getLineNumber()
+ ", column " + exception.getColumnNumber());
}
public void fatalError(SAXParseException exception) {
System.out.println("Fatal Error: " + exception.getMessage());
System.out.println(" at line " + exception.getLineNumber()
+ ", column " + exception.getColumnNumber());
}
}
Wild cards with anyName
and nsName
Exclusions with except
Redefining external content models when importing
Attribute or child element
Cannot declare entities
Parent models
Extra-document validation
ID-IDREF/key-keyref*
PSVI
* Can be added
and many more...
Converts between:
RELAX XML syntax
RELAX Compact syntax
DTDs
W3C XML Schema Language
$ trang http://www.w3.org/Graphics/SVG/1.2/rng/Full-1.2/Full-1.2.rng full.xsd http://www.w3.org/Graphics/SVG/1.2/rng/Tiny-1.2/tiny-structure.rng:401:13: warning: choice between attributes and children cannot be represented; approximating http://www.w3.org/Graphics/SVG/1.2/rng/Tiny-1.2/tiny-structure.rng:420:13: warning: choice between attributes and children cannot be represented; approximating http://www.w3.org/Graphics/SVG/1.2/rng/Tiny-1.2/tiny-structure.rng:438:13: warning: choice between attributes and children cannot be represented; approximating http://www.w3.org/Graphics/SVG/1.2/rng/Full-1.2/structure.rng:24:13: warning: choice between attributes and children cannot be represented; approximating http://www.w3.org/Graphics/SVG/1.2/rng/Tiny-1.2/script.rng:38:13: warning: choice between attributes and children cannot be represented; approximating http://www.w3.org/Graphics/SVG/1.2/rng/Tiny-1.2/tiny-flow.rng:22:15: warning: cannot represent an optional group of attributes; approximating http://www.w3.org/Graphics/SVG/1.2/rng/Tiny-1.2/handler.rng:44:13: warning: choice between attributes and children cannot be represented; approximating http://www.w3.org/Graphics/SVG/1.2/rng/Full-1.2/style.rng:81:13: warning: choice between attributes and children cannot be represented; approximating
RELAX NG
Eric van der Vlist
O'Reilly & Associates, 2004
ISBN 0-596-00421-4
This presentation: http://www.cafeconleche.org/slides/sd2007west/relaxng/
RELAX NG Tutorial: http://www.oasis-open.org/committees/relax-ng/tutorial.html