RELAX NG Tutorial

1. Getting started

Consider a simpleXML 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 beas follows:

<!DOCTYPEaddressBook [

<!ELEMENTaddressBook (card*)>

<!ELEMENT card(name, email)>

<!ELEMENT name(#PCDATA)>

<!ELEMENT email(#PCDATA)>

]>

A RELAX NG patternfor this could be written as follows:

<elementname="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>

If the addressBook is requiredto be non-empty, then we can use oneOrMore instead of zeroOrMore:

<elementname="addressBook"xmlns="http://relaxng.org/ns/structure/1.0">

  <oneOrMore>

    <element name="card">

      <element name="name">

        <text/>

      </element>

      <element name="email">

        <text/>

      </element>

    </element>

  </oneOrMore>

</element>

Now let's changeit to allow each card to have anoptional note element:

<elementname="addressBook" xmlns="http://relaxng.org/ns/structure/1.0">

  <zeroOrMore>

    <element name="card">

      <element name="name">

        <text/>

      </element>

      <element name="email">

        <text/>

      </element>

      <optional>

        <element name="note">

         <text/>

        </element>

      </optional>

    </element>

  </zeroOrMore>

</element>

Note thatthe text patternmatches arbitrary text, including empty text. Note also that whitespaceseparating tags is ignored when matching against a pattern.

All the elements specifyingthe pattern must be namespace qualified by the namespace URI:

http://relaxng.org/ns/structure/1.0

The examples aboveuse a default namespace declaration xmlns="http://relaxng.org/ns/structure/1.0" for this. Anamespace prefix is equally acceptable:

<rng:elementname="addressBook"xmlns:rng="http://relaxng.org/ns/structure/1.0">

  <rng:zeroOrMore>

    <rng:element name="card">

      <rng:element name="name">

        <rng:text/>

      </rng:element>

      <rng:elementname="email">

        <rng:text/>

      </rng:element>

    </rng:element>

  </rng:zeroOrMore>

</rng:element>

For the remainderof this document, the default namespace declaration will be left out ofexamples.

2. Choice

Now suppose wewant to allow the name to be brokendown into a givenName and a familyName, allowingan addressBook like this:

<addressBook>

  <card>

    <givenName>John</givenName>

    <familyName>Smith</familyName>

    <email>js@example.com</email>

  </card>

  <card>

    <name>Fred Bloggs</name>

    <email>fb@example.net</email>

  </card>

</addressBook>

We can use thefollowing pattern:

<elementname="addressBook">

  <zeroOrMore>

    <element name="card">

      <choice>

        <element name="name">

          <text/>

        </element>

        <group>

          <elementname="givenName">

            <text/>

          </element>

          <elementname="familyName">

            <text/>

          </element>

        </group>

      </choice>

      <element name="email">

        <text/>

      </element>

      <optional>

        <element name="note">

         <text/>

        </element>

      </optional>

    </element>

  </zeroOrMore>

</element>

This correspondsto the following DTD:

<!DOCTYPEaddressBook [

<!ELEMENTaddressBook (card*)>

<!ELEMENT card((name | (givenName, familyName)), email, note?)>

<!ELEMENT name(#PCDATA)>

<!ELEMENT email(#PCDATA)>

<!ELEMENTgivenName (#PCDATA)>

<!ELEMENTfamilyName (#PCDATA)>

<!ELEMENT note(#PCDATA)>

]>

3. Attributes

Suppose we wantthe card element tohave attributes rather than child elements. The DTD might look like this:

<!DOCTYPEaddressBook [

<!ELEMENTaddressBook (card*)>

<!ELEMENT cardEMPTY>

<!ATTLIST card

  name CDATA #REQUIRED

  email CDATA #REQUIRED>

]>

Just changeeach element pattern toan attribute pattern:

<element name="addressBook">

  <zeroOrMore>

    <element name="card">

      <attribute name="name">

        <text/>

      </attribute>

      <attribute name="email">

        <text/>

      </attribute>

    </element>

  </zeroOrMore>

</element>

In XML, the orderof attributes is traditionally not significant. RELAX NG follows thistradition. The above pattern would match both

<cardname="John Smith" email="js@example.com"/>

and

<cardemail="js@example.com" name="John Smith"/>

In contrast, theorder of elements is significant. The pattern

<elementname="card">

  <element name="name">

    <text/>

  </element>

  <element name="email">

    <text/>

  </element>

</element>

would not match

<card><email>js@example.com</email><name>JohnSmith</name></card>

Note that an attribute element byitself indicates a required attribute, just as an element element by itself indicates arequired element. To specify an optional attribute, use optional just aswith element:

<elementname="addressBook">

  <zeroOrMore>

    <element name="card">

      <attribute name="name">

        <text/>

      </attribute>

      <attribute name="email">

        <text/>

      </attribute>

      <optional>

        <attribute name="note">

          <text/>

        </attribute>

      </optional>

    </element>

  </zeroOrMore>

</element>

The group and choice patterns canbe applied to attribute patterns inthe same way they are applied to element patterns. For example, if we wanted to allow either aname attribute or both a givenName and a familyName attribute,we can specify this in the same way that we would if we were using elements:

<elementname="addressBook">

  <zeroOrMore>

    <element name="card">

      <choice>

        <attribute name="name">

          <text/>

        </attribute>

        <group>

          <attributename="givenName">

            <text/>

          </attribute>

          <attributename="familyName">

            <text/>

          </attribute>

        </group>

      </choice>

      <attribute name="email">

        <text/>

      </attribute>

    </element>

  </zeroOrMore>

</element>

The group and choice patterns cancombine element and attribute patternswithout restriction. For example, the following pattern would allow a choice ofelements and attributes independently for both the name and the email part ofa card:

<elementname="addressBook">

  <zeroOrMore>

    <element name="card">

      <choice>

        <element name="name">

         <text/>

        </element>

        <attribute name="name">

         <text/>

        </attribute>

      </choice>

      <choice>

        <element name="email">

         <text/>

        </element>

        <attribute name="email">

         <text/>

        </attribute>

      </choice>

    </element>

  </zeroOrMore>

</element>

As usual, therelative order of elements is significant, but the relative order of attributesis not. Thus the above would match any of:

<cardname="John Smith" email="js@example.com"/>

<cardemail="js@example.com" name="John Smith"/>

<cardemail="js@example.com"><name>JohnSmith</name></card>

<cardname="JohnSmith"><email>js@example.com</email></card>

<card><name>JohnSmith</name><email>js@example.com</email></card>

However, it wouldnot match

<card><email>js@example.com</email><name>JohnSmith</name></card>

because thepattern for card requiresany email childelement to follow any name childelement.

There is onedifference between attribute and element patterns: <text/> is thedefault for the content of an attribute pattern, whereas an element pattern is not allowed to be empty. For example,

<attributename="email"/>

is short for

<attributename="email">

  <text/>

</attribute>

It might seemnatural that

<elementname="x"/>

matched an x element with no attributes and nocontent. However, this would make the meaning of empty content inconsistentbetween the element pattern andtheattribute pattern, soRELAX NG does not allow the element pattern tobe empty. A pattern that matches an element with no attributes and no childrenmust use<empty/> explicitly:

<elementname="addressBook">

  <zeroOrMore>

    <element name="card">

      <element name="name">

        <text/>

      </element>

      <element name="email">

        <text/>

      </element>

      <optional>

        <elementname="prefersHTML">

          <empty/>

        </element>

      </optional>

    </element>

  </zeroOrMore>

</element>

Even if thepattern in an element patternmatches attributes only, there is no need to use empty. For example,

<elementname="card">

  <attribute name="email">

    <text/>

  </attribute>

</element>

is equivalent to

<elementname="card">

  <attribute name="email">

    <text/>

  </attribute>

  <empty/>

</element>

4. Named patterns

For a non-trivialRELAX NG pattern, it is often convenient to be able to give names to parts ofthe pattern. Instead of

<elementname="addressBook">

  <zeroOrMore>

    <element name="card">

      <element name="name">

        <text/>

      </element>

      <element name="email">

        <text/>

      </element>

    </element>

  </zeroOrMore>

</element>

we can write

<grammar>

 

  <start>

    <elementname="addressBook">

      <zeroOrMore>

        <element name="card">

         <ref name="cardContent"/>

        </element>

      </zeroOrMore>

    </element>

  </start>

 

  <define name="cardContent">

    <element name="name">

      <text/>

    </element>

    <element name="email">

      <text/>

    </element>

  </define>

 

</grammar>

A grammar element hasa single start childelement, and zero or more define childelements. The start and define elementscontain patterns. These patterns can contain ref elements that refer to patternsdefined by any of the define elements inthat grammar element.A grammar pattern ismatched by matching the pattern contained in the start element.

We can usethe grammar element towrite patterns in a style similar to DTDs:

<grammar>

 

  <start>

    <ref name="AddressBook"/>

  </start>

 

  <define name="AddressBook">

    <elementname="addressBook">

      <zeroOrMore>

        <ref name="Card"/>

      </zeroOrMore>

    </element>

  </define>

 

  <define name="Card">

    <element name="card">

      <ref name="Name"/>

      <ref name="Email"/>

    </element>

  </define>

 

  <define name="Name">

    <element name="name">

      <text/>

    </element>

  </define>

 

  <define name="Email">

    <element name="email">

      <text/>

    </element>

  </define>

 

</grammar>

Recursivereferences are allowed. For example,

<definename="inline">

  <zeroOrMore>

    <choice>

      <text/>

      <element name="bold">

        <ref name="inline"/>

      </element>

      <element name="italic">

        <ref name="inline"/>

      </element>

      <element name="span">

        <optional>

          <attributename="style"/>

        </optional>

        <ref name="inline"/>

      </element>

    </choice>

  </zeroOrMore>

</define>

However, recursivereferences must be within an element. Thus, the following is not allowed:

<definename="inline">

  <choice>

    <text/>

    <element name="bold">

      <ref name="inline"/>

    </element>

    <element name="italic">

      <ref name="inline"/>

    </element>

    <element name="span">

      <optional>

        <attributename="style"/>

      </optional>

      <ref name="inline"/>

    </element>

  </choice>

  <optional>

    <ref name="inline"/>

  </optional>

</define>

5. Datatyping

RELAX NG allowspatterns to reference externally-defined datatypes, such as those definedby [W3C XML Schema Datatypes]. RELAX NGimplementations may differ in what datatypes they support. You must usedatatypes that are supported by the implementation you plan to use.

The data pattern matches a string thatrepresents a value of a named datatype. The datatypeLibrary attribute contains a URI identifyingthe library of datatypes being used. The datatype library defined by [W3C XML Schema Datatypes] would beidentified by the URI http://www.w3.org/2001/XMLSchema-datatypes. The type attribute specifies the name of thedatatype in the library identified by the datatypeLibrary attribute. For example, if a RELAXNG implementation supported the datatypes of[W3C XML Schema Datatypes], you could use:

<elementname="number">

  <data type="integer"datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>

</element>

It is inconvenientto specify the datatypeLibrary attribute onevery data element, soRELAX NG allows the datatypeLibrary attribute tobe inherited. ThedatatypeLibrary attributecan be specified on any RELAX NG element. If a data element does not have a datatypeLibrary attribute,it will use the value from the closest ancestor that has a datatypeLibrary attribute.Typically, the datatypeLibrary attribute isspecified on the root element of the RELAX NG pattern. For example,

<elementname="point"datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">

  <element name="x">

    <data type="double"/>

  </element>

  <element name="y">

    <data type="double"/>

  </element>

</element>

If the children ofan element or an attribute match a data pattern, then complete content ofthe element or attribute must match that data pattern. It is not permitted to havea pattern which allows part of the content to match a data pattern, and another part to matchanother pattern. For example, the following pattern is not allowed:

<elementname="bad">

  <data type="int"/>

  <element name="note">

    <text/>

  </element>

</element>

However, this wouldbe fine:

<elementname="ok">

  <data type="int"/>

  <attribute name="note">

    <text/>

  </attribute>

</element>

Note that thisrestriction does not apply to the text pattern.

Datatypes may haveparameters. For example, a string datatype may have a parameter controlling thelength of the string. The parameters applicable to any particular datatype aredetermined by the datatyping vocabulary. Parameters are specified by adding oneor more param elements aschildren of the data element. Forexample, the following constrains the email element to contain a string at most127 characters long:

<elementname="email">

  <data type="string">

    <paramname="maxLength">127</param>

  </data>

</element>

6. Enumerations

Many markupvocabularies have attributes whose value is constrained to be one of set ofspecified values. The value patternmatches a string that has a specified value. For example,

<elementname="card">

  <attribute name="name"/>

  <attribute name="email"/>

  <attributename="preferredFormat">

    <choice>

      <value>html</value>

      <value>text</value>

    </choice>

  </attribute>

</element>

allows the preferredFormat attribute tohave the value html or text. This corresponds to the DTD:

<!DOCTYPE card [

<!ELEMENT cardEMPTY>

<!ATTLIST card

  name CDATA #REQUIRED

  email CDATA #REQUIRED

  preferredFormat (html|text) #REQUIRED>

]>

The value pattern isnot restricted to attribute values. For example, the following is allowed:

<elementname="card">

  <element name="name">

    <text/>

  </element>

  <element name="email">

    <text/>

  </element>

  <elementname="preferredFormat">

    <choice>

      <value>html</value>

      <value>text</value>

    </choice>

  </element>

</element>

The prohibitionagainst a data pattern'smatching only part of the content of an element also applies to value patterns.

By default,the value pattern willconsider the string in the pattern to match the string in the document if thetwo strings are the same after the whitespace in both strings is normalized.Whitespace normalization strips leading and trailing whitespace characters, andcollapses sequences of one or more whitespace characters to a single spacecharacter. This corresponds to the behaviour of an XML parser for an attributethat is declared as other than CDATA. Thus the above pattern will match any of:

<cardname="John Smith" email="js@example.com"preferredFormat="html"/>

<cardname="John Smith" email="js@example.com"preferredFormat="  html  "/>

The way thatthe value patterncompares the pattern string with the document string can be controlled byspecifying a type attributeand optionally adatatypeLibrary attribute,which identify a datatype in the same way as for the data pattern. The pattern string matchesthe document string if they both represent the same value of the specifieddatatype. Thus, whereas the data patternmatches an arbitrary value of a datatype, the value pattern matches a specific value ofa datatype.

If there is noancestor element with a datatypeLibrary element, thedatatype library defaults to a built-in RELAX NG datatype library. Thisprovides two datatypes,string and token. The built-indatatype token correspondsto the default comparison behavior of the value pattern. The built-in datatype string comparesstrings without any whitespace normalization (other than the end-of-line andattribute value normalization automatically performed by XML). For example,

<elementname="card">

  <attribute name="name"/>

  <attribute name="email"/>

  <attributename="preferredFormat">

    <choice>

      <valuetype="string">html</value>

      <valuetype="string">text</value>

    </choice>

  </attribute>

</element>

will not match

<cardname="John Smith" email="js@example.com"preferredFormat="  html  "/>

7. Lists

The list pattern matches a whitespace-separatedsequence of tokens; it contains a pattern that the sequence of individualtokens must match. The list patternsplits a string into a list of strings, and then matches the resulting list ofstrings against the pattern inside the list pattern.

For example,suppose we want to have a vector element thatcontains two floating point numbers separated by whitespace. We could use list as follows:

<elementname="vector">

  <list>

    <data type="float"/>

    <data type="float"/>

  </list>

</element>

Or suppose we wantthe vector element tocontain a list of one or more floating point numbers separated by whitespace:

<elementname="vector">

  <list>

    <oneOrMore>

      <data type="double"/>

    </oneOrMore>

  </list>

</element>

Or suppose we wanta path elementcontaining an even number of floating point numbers:

<elementname="path">

  <list>

    <oneOrMore>

      <data type="double"/>

      <data type="double"/>

    </oneOrMore>

  </list>

</element>

8. Interleaving

The interleave pattern allowschild elements to occur in any order. For example, the following would allowthe card element tocontain the name and email elements inany order:

<elementname="addressBook">

  <zeroOrMore>

    <element name="card">

      <interleave>

        <element name="name">

         <text/>

        </element>

        <element name="email">

         <text/>

        </element>

      </interleave>

    </element>

  </zeroOrMore>

</element>

The pattern iscalled interleave because ofhow it works with patterns that match more than one element. Suppose we want towrite a pattern for the HTML headelement which requires exactly one title element, at most one base element and zero or more style, script, link and meta elements and suppose we are writinga grammarpattern that hasone definition for each element. Then we could define the pattern for head as follows:

<definename="head">

  <element name="head">

    <interleave>

      <ref name="title"/>

      <optional>

        <ref name="base"/>

      </optional>

      <zeroOrMore>

        <ref name="style"/>

      </zeroOrMore>

      <zeroOrMore>

        <ref name="script"/>

      </zeroOrMore>

      <zeroOrMore>

        <ref name="link"/>

      </zeroOrMore>

      <zeroOrMore>

        <ref name="meta"/>

      </zeroOrMore>

    </interleave>

  </element>

</define>

Suppose we hada head element thatcontained a meta element,followed by a title element,followed by a meta element.This would match the pattern because it is an interleaving of a sequence oftwo meta elements,which match the child pattern

      <zeroOrMore>

        <ref name="meta"/>

      </zeroOrMore>

and a sequence ofone title element,which matches the child pattern

      <ref name="title"/>

The semantics ofthe interleave pattern arethat a sequence of elements matches an interleave pattern if it is an interleaving ofsequences that match the child patterns of the interleave pattern. Note that this is differentfrom the & connector inSGML: A* & B matches thesequence of elements A A B or thesequence of elements B A A but not thesequence of elements A B A.

One special caseof interleave is verycommon: interleaving <text/> with apattern p represents apattern that matches what p matches but also allows characters to occur as children. The mixed element is ashorthand for this.

<mixed> p</mixed>

is short for

<interleave><text/> p </interleave>

9. Modularity

9.1. Referencing external patterns

The externalRef pattern canbe used to reference a pattern defined in a separate file. The externalRef element hasa required href attributethat specifies the URL of a file containing the pattern. The externalRef matches ifthe pattern contained in the specified URL matches. Suppose for example, youhave a RELAX NG pattern that matches HTML inline content stored in inline.rng:

<grammar>

  <start>

    <ref name="inline"/>

  </start>

 

  <define name="inline">

    <zeroOrMore>

      <choice>

        <text/>

        <element name="code">

          <ref name="inline"/>

        </element>

        <element name="em">

          <ref name="inline"/>

        </element>

        <!-- etc -->

      </choice>

    </zeroOrMore>

  </define>

</grammar>

Then we couldallow the note element tocontain inline HTML markup by using externalRef as follows:

<elementname="addressBook">

  <zeroOrMore>

    <element name="card">

      <element name="name">

        <text/>

      </element>

      <element name="email">

        <text/>

      </element>

      <optional>

        <element name="note">

         <externalRef href="inline.rng"/>

        </element>

      </optional>

    </element>

  </zeroOrMore>

</element>

For anotherexample, suppose you have two RELAX NG patterns stored in files pattern1.rng and pattern2.rng. Then thefollowing is a pattern that matches anything matched by either of thosepatterns:

<choice>

  <externalRefhref="pattern1.rng"/>

  <externalRefhref="pattern2.rng"/>

</choice>

9.2. Combining definitions

If a grammarcontains multiple definitions with the same name, then the definitions mustspecify how they are to be combined into a single definition by using the combine attribute.The combine attributemay have the value choice or interleave. For example,

<definename="inline.class" combine="choice">

  <element name="bold">

    <ref name="inline"/>

  </element>

</define>

 

<definename="inline.class" combine="choice">

  <element name="italic">

    <ref name="inline"/>

  </element>

</define>

is equivalent to

<definename="inline.class">

  <choice>

    <element name="bold">

      <ref name="inline"/>

    </element>

    <element name="italic">

      <ref name="inline"/>

    </element>

  </choice>

</define>

When combiningattributes, combine="interleave" is typicallyused. For example,

<grammar>

 

  <start>

    <elementname="addressBook">

      <zeroOrMore>

        <element name="card">

         <ref name="card.attlist"/>

        </element>

      </zeroOrMore>

    </element>

  </start>

 

  <define name="card.attlist"combine="interleave">

    <attribute name="name">

      <text/>

    </attribute>

  </define>

 

  <define name="card.attlist"combine="interleave">

    <attribute name="email">

      <text/>

    </attribute>

  </define>

 

</grammar>

is equivalent to

<grammar>

 

  <start>

    <elementname="addressBook">

      <zeroOrMore>

        <element name="card">

         <ref name="card.attlist"/>

        </element>

      </zeroOrMore>

    </element>

  </start>

 

  <define name="card.attlist">

    <interleave>

      <attribute name="name">

        <text/>

      </attribute>

      <attribute name="email">

        <text/>

      </attribute>

    </interleave>

  </define>

 

</grammar>

which isequivalent to

<grammar>

 

  <start>

    <elementname="addressBook">

      <zeroOrMore>

        <element name="card">

         <ref name="card.attlist"/>

        </element>

      </zeroOrMore>

    </element>

  </start>

 

  <define name="card.attlist">

    <group>

      <attribute name="name">

        <text/>

      </attribute>

      <attribute name="email">

        <text/>

      </attribute>

    </group>

  </define>

 

</grammar>

since combiningattributes with interleave has the sameeffect as combining them with group.

It is an error fortwo definitions of the same name to specify different values for combine. Note that theorder of definitions within a grammar is not significant.

Multiple start elements canbe combined in the same way as multiple definitions.

9.3. Merging grammars

The include elementallows grammars to be merged together. A grammar pattern may have include elements aschildren. An include element hasa required href attributethat specifies the URL of a file containing a grammar pattern. The definitions in thereferenced grammar pattern willbe included in grammar patterncontaining theinclude element.

The combine attribute isparticularly useful in conjunction with include. For example, suppose a RELAX NGpattern inline.rng provides apattern for inline content, which allows bold and italic elements arbitrarily nested:

<grammar>

 

  <define name="inline">

    <zeroOrMore>

      <refname="inline.class"/>

    </zeroOrMore>

  </define>

 

  <define name="inline.class">

    <choice>

      <text/>

      <element name="bold">

        <ref name="inline"/>

      </element>

      <element name="italic">

        <ref name="inline"/>

      </element>

    </choice>

  </define>

 

</grammar>

Another RELAX NGpattern could use inline.rng andadd code and em to the set of inline elements asfollows:

<grammar>

 

  <include href="inline.rng"/>

 

  <start>

    <element name="doc">

      <zeroOrMore>

        <element name="p">

         <ref name="inline"/>

        </element>

      </zeroOrMore>

    </element>

  </start>

 

  <define name="inline.class"combine="choice">

    <choice>

      <element name="code">

        <ref name="inline">

      </element>

      <element name="em">

        <ref name="inline">

      </element>

    </choice>

  </define>

 

</grammar>

This would beequivalent to

<grammar>

 

  <define name="inline">

    <zeroOrMore>

      <refname="inline.class"/>

    </zeroOrMore>

  </define>

 

  <define name="inline.class">

    <choice>

      <text/>

      <element name="bold">

        <ref name="inline"/>

      </element>

      <element name="italic">

        <ref name="inline"/>

      </element>

    </choice>

  </define>

 

  <start>

    <element name="doc">

      <zeroOrMore>

        <element name="p">

         <ref name="inline"/>

        </element>

      </zeroOrMore>

    </element>

  </start>

 

  <define name="inline.class"combine="choice">

    <choice>

      <element name="code">

        <ref name="inline">

      </element>

      <element name="em">

        <ref name="inline">

      </element>

    </choice>

  </define>

 

</grammar>

which isequivalent to

<grammar>

 

  <define name="inline">

    <zeroOrMore>

      <refname="inline.class"/>

    </zeroOrMore>

  </define>

 

  <define name="inline.class">

    <choice>

      <text/>

      <element name="bold">

        <ref name="inline"/>

      </element>

      <element name="italic">

        <ref name="inline"/>

      </element>

      <element name="code">

        <ref name="inline">

      </element>

      <element name="em">

        <ref name="inline">

      </element>

    </choice>

  </define>

 

  <start>

    <element name="doc">

      <zeroOrMore>

        <element name="p">

         <ref name="inline"/>

        </element>

      </zeroOrMore>

    </element>

  </start>

 

</grammar>

Note that it isallowed for one of the definitions of a name to omit the combine attribute.However, it is an error if there is more than one definition that does so.

The notAllowed pattern isuseful when merging grammars. The notAllowed pattern never matches anything. Justas adding empty to a group makes nodifference, so addingnotAllowed to a choice makes nodifference. It is typically used to allow an including pattern to specifyadditional choices with combine="choice". For example, ifinline.rng were writtenlike this:

<grammar>

 

  <define name="inline">

    <zeroOrMore>

      <choice>

        <text/>

        <element name="bold">

         <ref name="inline"/>

        </element>

        <element name="italic">

         <ref name="inline"/>

        </element>

        <refname="inline.extra"/>

      </choice>

    </zeroOrMore>

  </define>

 

  <define name="inline.extra">

    <notAllowed/>

  </define>

 

</grammar>

then it could be customizedto allow inline code and em elements as follows:

<grammar>

 

  <include href="inline.rng"/>

 

  <start>

    <element name="doc">

      <zeroOrMore>

        <element name="p">

         <ref name="inline"/>

        </element>

      </zeroOrMore>

    </element>

  </start>

 

  <define name="inline.extra"combine="choice">

    <choice>

      <element name="code">

        <ref name="inline">

      </element>

      <element name="em">

        <ref name="inline">

      </element>

    </choice>

  </define>

 

</grammar>

9.4. Replacing definitions

RELAX NGallows define elements tobe put inside the include element toindicate that they are to replace definitions in the included grammar pattern.

Suppose thefile addressBook.rng contains:

<grammar>

 

  <start>

    <elementname="addressBook">

      <zeroOrMore>

        <element name="card">

         <ref name="cardContent"/>

        </element>

      </zeroOrMore>

    </element>

  </start>

 

  <define name="cardContent">

    <element name="name">

      <text/>

    </element>

    <element name="email">

      <text/>

    </element>

  </define>

 

</grammar>

Suppose we wish tomodify this pattern so that the card element contains an emailAddress element instead of an email element. Then we could replace thedefinition of cardContent as follows:

<grammar>

 

  <includehref="addressBook.rng">

 

    <define name="cardContent">

      <element name="name">

        <text/>

      </element>

      <elementname="emailAddress">

        <text/>

      </element>

    </define>

 

  </include>

 

</grammar>

This would be equivalentto

<grammar>

 

  <start>

    <elementname="addressBook">

      <zeroOrMore>

        <element name="card">

         <ref name="cardContent"/>

        </element>

      </zeroOrMore>

    </element>

  </start>

 

  <define name="cardContent">

    <element name="name">

      <text/>

    </element>

    <elementname="emailAddress">

      <text/>

    </element>

  </define>

 

</grammar>

An include element canalso contain a start element,which replaces the start in theincluded grammar pattern.

10. Namespaces

RELAX NG is namespace-aware.Thus, it considers an element or attribute to have both a local name and anamespace URI which together constitute the name of that element or attribute.

10.1. Using the ns attribute

The element pattern usesan ns attribute tospecify the namespace URI of the elements that it matches. For example,

<elementname="foo" ns="http://www.example.com">

  <empty/>

</element>

would match anyof:

<fooxmlns="http://www.example.com"/>

<e:fooxmlns:e="http://www.example.com"/>

<example:fooxmlns:example="http://www.example.com"/>

but not any of:

<foo/>

<e:fooxmlns:e="http://WWW.EXAMPLE.COM"/>

<example:fooxmlns:example="http://www.example.net"/>

A value of anempty string for the ns attributeindicates a null or absent namespace URI (just as with the xmlns attribute).Thus, the pattern

<elementname="foo" ns="">

  <empty/>

</element>

matches any of:

<fooxmlns=""/>

<foo/>

but not any of:

<fooxmlns="http://www.example.com"/>

<e:fooxmlns:e="http://www.example.com"/>

It is tedious anderror-prone to specify the ns attribute onevery element, so RELAX NGallows it to be defaulted. If an element pattern does not specify an nsattribute, then it defaults to the valueof the ns attribute ofthe nearest ancestor that has an ns attribute, or the empty string if there is no such ancestor. Thus,

<elementname="addressBook">

  <zeroOrMore>

    <element name="card">

      <element name="name">

        <text/>

      </element>

      <element name="email">

        <text/>

      </element>

    </element>

  </zeroOrMore>

</element>

is equivalent to

<elementname="addressBook" ns="">

  <zeroOrMore>

    <element name="card"ns="">

      <element name="name"ns="">

        <text/>

      </element>

      <element name="email"ns="">

        <text/>

      </element>

    </element>

  </zeroOrMore>

</element>

and

<elementname="addressBook" ns="http://www.example.com">

  <zeroOrMore>

    <element name="card">

      <element name="name">

        <text/>

      </element>

      <element name="email">

        <text/>

      </element>

    </element>

  </zeroOrMore>

</element>

is equivalent to

<elementname="addressBook" ns="http://www.example.com">

  <zeroOrMore>

    <element name="card"ns="http://www.example.com">

      <element name="name"ns="http://www.example.com">

        <text/>

      </element>

      <element name="email"ns="http://www.example.com">

        <text/>

      </element>

    </element>

  </zeroOrMore>

</element>

The attribute pattern alsotakes an ns attribute.However, there is a difference in how it defaults. This is because of the factthat the XML Namespaces Recommendation does not apply the default namespace toattributes. If an ns attribute isnot specified on the attribute pattern,then it defaults to the empty string. Thus,

<element name="addressBook"ns="http://www.example.com">

  <zeroOrMore>

    <element name="card">

      <attribute name="name"/>

      <attribute name="email"/>

    </element>

  </zeroOrMore>

</element>

is equivalent to

<elementname="addressBook" ns="http://www.example.com">

  <zeroOrMore>

    <element name="card"ns="http://www.example.com">

      <attribute name="name"ns=""/>

      <attribute name="email"ns=""/>

    </element>

  </zeroOrMore>

</element>

and so will match

<addressBookxmlns="http://www.example.com">

  <card name="John Smith"email="js@example.com"/>

</addressBook>

or

<example:addressBookxmlns:example="http://www.example.com">

  <example:card name="John Smith"email="js@example.com"/>

</example:addressBook>

but not

<example:addressBookxmlns:example="http://www.example.com">

  <example:card example:name="JohnSmith" example:email="js@example.com"/>

</example:addressBook>

10.2. Qualified names

When a patternmatches elements and attributes from multiple namespaces, using the ns attribute would require repeatingnamespace URIs in different places in the pattern. This is error-prone and hardto maintain, so RELAX NG also allows the element and attribute patterns to use a prefix in thevalue of the name attribute tospecify the namespace URI. In this case, the prefix specifies the namespace URIto which that prefix is bound by the namespace declarations in scope on theelement or attribute pattern.Thus,

<elementname="ab:addressBook"xmlns:ab="http://www.example.com/addressBook"

                               xmlns:a="http://www.example.com/address">

  <zeroOrMore>

    <element name="ab:card">

      <element name="a:name">

        <text/>

      </element>

      <element name="a:email">

        <text/>

      </element>

    </element>

  </zeroOrMore>

</element>

is equivalent to

<elementname="addressBook"ns="http://www.example.com/addressBook">

  <zeroOrMore>

    <element name="card"ns="http://www.example.com/addressBook">

      <element name="name"ns="http://www.example.com/address">

       <text/>

      </element>

      <element name="email"ns="http://www.example.com/address">

        <text/>

      </element>

    </element>

  </zeroOrMore>

</element>

If a prefix isspecified in the value of the name attribute of an element or attribute pattern,then that prefix determines the namespace URI of the elements or attributesthat will be matched by that pattern, regardless of the value of any ns attribute.

Note that the XMLdefault namespace (as specified by the xmlns attribute) is not used indetermining the namespace URI of elements and attributes that element andattribute patternsmatch.

11. Name classes

Normally, the nameof the element to be matched by an element element is specified by a name attribute. An element element caninstead start with an element specifying a name-class. In thiscase, the element pattern willonly match an element if the name of the element is a member of the name-class.The simplest name-class is anyName, which any nameat all is a member of, regardless of its local name and its namespace URI. Forexample, the following pattern matches any well-formed XML document:

<grammar>

 

  <start>

    <ref name="anyElement"/>

  </start>

 

  <define name="anyElement">

    <element>

      <anyName/>

      <zeroOrMore>

        <choice>

         <attribute>

           <anyName/>

         </attribute>

         <text/>

         <ref name="anyElement"/>

        </choice>

      </zeroOrMore>

    </element>

  </define>

 

</grammar>

The nsName name-classcontains any name with the namespace URI specified by the ns attribute, which defaults in thesame way as the ns attribute onthe elementpattern.

The choice name-classmatches any name that is a member of any of its child name-classes.

The anyName and nsName name-classescan contain an except clause. Forexample,

<element name="card"ns="http://www.example.com">

  <zeroOrMore>

    <attribute>

      <anyName>

        <except>

          <nsName/>

          <nsName ns=""/>

        </except>

      </anyName>

    </attribute>

  </zeroOrMore>

  <text/>

</element>

would allowthe card element tohave any number of namespace-qualified attributes provided that they werequalified with namespace other than that of the cardelement.

Note that an attribute patternmatches a single attribute even if it has a name-class that contains multiple names.To match zero or more attributes, the zeroOrMoreelement must be used.

The name name-class contains a single name.The content of the name elementspecifies the name in the same way as the name attribute of the element pattern.The nsattribute specifiesthe namespace URI in the same way as the element pattern.

Some schemalanguages have a concept of lax validation, where an elementor attribute is validated against a definition only if there is one. We canimplement this concept in RELAX NG with name classes that uses except and name. Suppose, for example, we wanted to allowan element to have any attribute with a qualified name, but we still wanted toensure that if there was an xml:space attribute,it had the value default or preserve. It wouldn't workto use

<elementname="example">

  <zeroOrMore>

    <attribute>

      <anyName/>

    </attribute>

  </zeroOrMore>

  <optional>

    <attributename="xml:space">

      <choice>

        <value>default</value>

        <value>preserve</value>

      </choice>

    </attribute>

  </optional>

</element>

because an xml:space attributewith a value other than default or preserve would match

    <attribute>

      <anyName/>

    </attribute>

even though it didnot match

    <attributename="xml:space">

      <choice>

        <value>default</value>

        <value>preserve</value>

      </choice>

    </attribute>

The solution is touse name togetherwith except:

<elementname="example">

  <zeroOrMore>

    <attribute>

      <anyName>

        <except>

          <name>xml:space</name>

        </except>

      </anyName>

    </attribute>

  </zeroOrMore>

  <optional>

    <attributename="xml:space">

      <choice>

        <value>default</value>

        <value>preserve</value>

      </choice>

    </attribute>

  </optional>

</element>

Note thatthe define elementcannot contain a name-class; it can only contain a pattern.

12. Annotations

If a RELAX NGelement has an attribute or child element with a namespace URI other than theRELAX NG namespace, then that attribute or element is ignored. Thus, you canadd annotations to RELAX NG patterns simply by using an attribute or element ina separate namespace:

<elementname="addressBook"xmlns="http://relaxng.org/ns/structure/1.0"xmlns:a="http://www.example.com/annotation">

  <zeroOrMore>

    <element name="card">

      <a:documentation>Information abouta single email address.</a:documentation>

      <element name="name">

        <text/>

      </element>

      <element name="email">

        <text/>

      </element>

    </element>

  </zeroOrMore>

</element>

RELAX NG alsoprovides a div elementwhich allows an annotation to be applied to a group of definitions in agrammar. For example, you might want to divide up the definitions of thegrammar into modules:

<grammar xmlns:m="http://www.example.com/module">

 

  <div m:name="inline">

 

    <define name="code"> pattern</define>

    <define name="em"> pattern</define>

    <define name="var"> pattern</define>

 

  </div>

 

  <div m:name="block">

 

    <define name="p"> pattern</define>

    <define name="ul"> pattern</define>

    <define name="ol"> pattern</define>

 

  </div>

 

</grammar>

This would allowyou easily to generate variants of the grammar based on a selection of modules.

A companionspecification, RELAX NG DTD Compatibility [Compatibility], definesannotations to implement some features of XML DTDs.

13. Nested grammars

There is noprohibition against nesting grammar patterns. A ref pattern refers to a definition fromnearest grammar ancestor.There is also a parentRef element thatescapes out of the current grammar and references a definition from the parentof the current grammar.

Imagine theproblem of writing a pattern for tables. The pattern for tables only caresabout the structure of tables; it doesn't care about what goes inside a tablecell. First, we create a RELAX NG pattern table.rng as follows:

<grammar>

 

<definename="cell.content">

  <notAllowed/>

</define>

 

<start>

  <element name="table">

    <oneOrMore>

      <element name="tr">

        <oneOrMore>

         <element name="td">

           <ref name="cell.content"/>

         </element>

        </oneOrMore>

      </element>

    </oneOrMore>

  </element>

</start>

 

</grammar>

Patterns thatinclude table.rng mustredefine cell.content. By using anested grammar patterncontaining a parentRef pattern, theincluding pattern can redefinecell.content to be apattern defined in the including pattern's grammar, thus effectively importinga pattern from the parent grammar into the child grammar:

<grammar>

 

<start>

  <element name="doc">

    <zeroOrMore>

      <choice>

        <element name="p">

         <ref name="inline"/>

        </element>

        <grammar>

         <include href="table.rng">

           <define name="cell.content">

             <parentRef name="inline"/>

           </define>

          </include>

        </grammar>

      </choice>

    </zeroOrMore>

  </element>

</start>

 

<definename="inline">

  <zeroOrMore>

    <choice>

      <text/>

      <element name="em">

        <ref name="inline"/>

      </element>

    </choice>

  </zeroOrMore>

</define>

 

</grammar>

Of course, in atrivial case like this, there is no advantage in nesting the grammars: we couldsimply have included table.rng within theouter grammar element.However, when the included grammar has many definitions, nesting it avoids thepossibility of name conflicts between the including grammar and the includedgrammar.

14. Non-restrictions

RELAX NG does notrequire patterns to be "deterministic" or "unambiguous".

Suppose we wantedto write the email address book in HTML, but use class attributes to specifythe structure:

<elementname="html">

  <element name="head">

    <element name="title">

      <text/>

    </element>

  </element>

  <element name="body">

    <element name="table">

      <attribute name="class">

        <value>addressBook</value>

      </attribute>

      <oneOrMore>

        <element name="tr">

         <attribute name="class">

           <value>card</value>

         </attribute>

          <element name="td">

           <attribute name="class">

             <value>name</value>

           </attribute>

            <interleave>

              <text/>

              <optional>

                <elementname="span">

                  <attributename="class">

                    <value>givenName</value>

                  </attribute>

                  <text/>

                </element>

              </optional>

              <optional>

                <elementname="span">

                  <attributename="class">

                    <value>familyName</value>

                  </attribute>

                  <text/>

                </element>

              </optional>

            </interleave>

          </element>

          <element name="td">

           <attribute name="class">

             <value>email</value>

           </attribute>

            <text/>

          </element>

        </element>

      </oneOrMore>

    </element>

  </element>

</element>

This would match aXML document such as:

<html>

  <head>

    <title>Example AddressBook</title>

  </head>

  <body>

    <table class="addressBook">

      <tr class="card">

        <td class="name">

          <spanclass="givenName">John</span>

          <spanclass="familyName">Smith</span>

        </td>

        <tdclass="email">js@example.com</td>

      </tr>

    </table>

  </body>

</html>

but not:

<html>

  <head>

    <title>Example AddressBook</title>

  </head>

  <body>

    <table class="addressBook">

      <tr class="card">

        <td class="name">

          <spanclass="givenName">John</span>

          <!-- Note the incorrect classattribute -->

          <spanclass="givenName">Smith</span>

        </td>

        <tdclass="email">js@example.com</td>

      </tr>

    </table>

  </body>

</html>

15. Further information

The definitivespecification of RELAX NG is [RELAX NG].

A. Comparison with XML DTDs

RELAX NG providesfunctionality that goes beyond XML DTDs. In particular, RELAX NG

  • uses XML syntax to represent schemas
  • supports datatyping
  • integrates attributes into content models
  • supports XML namespaces
  • supports unordered content
  • supports context-sensitive content models

ID/IDREFvalidation is not provided by RELAX NG; however, it is provided by a companionspecification, RELAX NG DTD Compatibility [Compatibility]. Comprehensivesupport for cross-reference checking is planned for a future specification.

RELAX NG does notsupport features of XML DTDs that involve changing the infoset of an XMLdocument. In particular, RELAX NG

  • does not allow defaults for attributes to be specified; however, this is allowed by RELAX NG DTD Compatibility [Compatibility]
  • does not allow entities to be specified
  • does not allow notations to be specified
  • does not specify whether whitespace is significant

Also RELAX NG doesnot define a way for an XML document to associate itself with a RELAX NGpattern.

B. Comparison with RELAX Core

Any description inRELAX Core can be directly captured in RELAX NG without loss of information.

B.1. Mapping RELAX NG to RELAX Core

B.1.1. elementRule-tag pairs

An elementRule as well asthe referenced tag element istypically captured by a define elementcontaining an element element asthe child.

An elementRule-tag pair in RELAX Core is shown below:

<elementRulerole="foo" label="bar">

  hedge model

</elementRule>

<tagrole="foo" name="baz">

  attribute declarations

</tag>

A rewrite in RELAXNG is shown below:

<definename="bar">

  <element name="baz">

    hedge model

    attribute declarations

  </element>

</define>

B.1.2. hedgeRule

A hedgeRule element iscaptured by a define elementcontaining attribute declarations.

A hedgeRule element inRELAX Core is shown below:

<hedgeRulelabel="bar">

  hedge model

</hedgeRule>

A rewrite in RELAXNG is:

<definename="bar">

  hedge model

</define>

B.1.3. attPool

An attPool element inRELAX Core is shown below:

<attPoolrole="foo">

  attribute declarations

</attPool>

A rewrite in RELAXNG is

<definename="foo">

  attribute declarations

</define>

B.1.4. Hedge models

Mapping of hedgemodels in RELAX Core to RELAX NG is summarized below:

1.      occurs="*" in RELAXCore is captured by <zeroOrMore>...</zeroOrMore>.

2.      occurs="+" in RELAXCore is captured by <oneOrMore>...</oneOrMore>

3.      occurs="?" in RELAXCore is captured by <optional>...</optional>

4.      <mixed>...</mixed> in RELAX Coreis captured by <mixed>...</mixed>

5.      <ref label="..."/> in RELAXCore is captured by <refname="..."/>.

6.      <hedgeRef label="..."/> in RELAXCore is captured by <refname="..."/>

B.1.5. Attribute declarations

Both languagesuse attribute. However, inRELAX Core, an attribute without required="true" declares adefaultable attribute. On the other hand, in RELAX NG, a defaultable attributehas to be declared by an attribute elementwithin an optional element.

Declaration of arequired attribute in RELAX Core is shown below:

<attributename="foo" type="integer" required="true"/>

In RELAX NG, thisis captured by:

<attributename="foo">

  <data type="integer"/>

</attribute>

Declaration of anoptional attribute in RELAX Core is shown below:

<attributename="foo" type="integer"/>

In RELAX NG, thisis captured by:

<optional>

  <attribute name="foo">

    <data type="integer"/>

  </attribute>

</optional>

B.2. Examples

B.2.1. Ancestor-and-sibling-sensitivecontent models

Here is a rewriteof an example in STEP 7 of "HOWTO RELAX". The first paragraph cannot contain footnotes, but the otherparagraphs can.

<grammar>

  <start>

    <element name="doc">

      <refname="paraWithoutFNotes"/>

      <zeroOrMore>

        <refname="paraWithFNotes"/>

      </zeroOrMore>

    </element>

  </start>

 

  <definename="paraWithoutFNotes">

    <element name="para">

      <text/>

    </element>

  </define>

 

  <definename="paraWithFNotes">

    <element name="para">

      <mixed>

        <zeroOrMore>

          <elementname="fnote">

            <text/>

          </element>

        </zeroOrMore>

      </mixed>

    </element>

  </define>

 

</grammar>

The followingdocument matches this pattern:

<doc><para/><para><fnote/></para></doc>

On the other hand,the following document does not:

<doc><para><fnote/></para></doc>

B.2.2. Attribute-sensitive content model

Here is a rewriteof an example in STEP 8 of "HOWTO RELAX". This pattern assigns different content models for the same tagname div depending onthe value of the attribute class.

<grammar>

 

  <start>

    <element name="html">

      <zeroOrMore>

        <ref name="section"/>

      </zeroOrMore>

    </element>

  </start>

 

  <define name="section">

    <element name="div">

      <attributename="class"><value>section</value></attribute>

      <zeroOrMore>

        <element name="para">

          <text/>

        </element>

      </zeroOrMore>

      <zeroOrMore>

        <refname="subsection"/>

      </zeroOrMore>

   </element>

  </define>

 

  <define name="subsection">

    <element name="div">

      <attributename="class"><value>subsection</value></attribute>

      <zeroOrMore>

        <element name="para">

          <text/>

        </element>

      </zeroOrMore>

    </element>

  </define>

 

</grammar>

The followingdocument matches this pattern:

<html>

  <div class="section">

    <para/>

    <div class="subsection">

      <para/>

    </div>

  </div>

  <div class="section">

    <div class="subsection">

      <para/>

    </div>

  </div>

</html>

On the other hand,the following document does not:

<html>

  <div class="subsection">

    <para/>

    <div class="section">

      <para/>

    </div>

  </div>

</html>

B.3. Features of RELAX NG beyond RELAXCore

RELAX NG has somefeatures which are missing in RELAX Core.

1.     Namespaces: sinceRELAX Core is intended to be used in conjunction with RELAX Namespace, RELAXCore does not support namespaces. On the other hand, RELAX NG supportsnamespaces. RELAX Namespace will be extended so that it can work with RELAX NG.

2.     Mixture of element and attribute: RELAX Core doesnot allow their mixture but rather provide two types of basic constructs, namely elementRule/hedgeRule andtag/attPool.

3.     Name classes:RELAX Core does not have name classes but merely provide name literals.

4.     interleave: RELAX Core doesnot provide any mechanism for interleaving.

5.     Datatypelibraries: RELAX Core allows XML Schema Part 2 but does not allow otherdatatype libaries.

6.     define in include: RELAX Core doesnot allow such redefinitions.

7.     list: RELAX Core does not provide suchstructured strings.

8.     data in choice: in RELAX Core, the hedge model of elementRule is either adatatype reference or an expression without datatype references.

C. Comparison with TREX

RELAX NG has thefollowing changes from TREX:

1.     the concur pattern hasbeen removed

2.     the string pattern hasbeen replaced by the value pattern

3.     the anyString pattern hasbeen renamed to text

4.     the namespace URIis different

5.     pattern elementsmust be namespace qualified

6.     anonymousdatatypes have been removed

7.     the data pattern can have parametersspecified by param childelements

8.     the list pattern has been added for matchingwhitespace-separated lists of tokens

9.     the replace and group values forthe combine attributehave been removed

10. an include element in agrammar may contain define elementsthat replace included definitions

11. the restrictionthat definitions combined with the combine attribute must be from differentfiles has been removed

12. a div element may be used to grouptogether definitions within a grammar

13. an include elementoccurring as a pattern has been renamed to externalRef; an include element is now allowed only as achild of the grammar element

14. the parent attribute onthe ref element hasbeen replaced by a new parentRef element

15. the type attribute of the data element is an unqualified name;the data element usesthe datatypeLibrary attributerather than the ns attribute toidentify the namespace of the datatype

16. a start element isnot allowed to have a name attribute

17. an attribute element isnot allowed to have a global attribute

18. the not and difference name classes have been replacedby except

19. the data element may have an except child

D. Changes from 12 June 2001 version

1.     key and keyRef have been removed; support for IDand IDREF is now available in a companion specification, RELAX NG DTDCompatibility Annotations[Compatibility]

2.     difference and not have been replaced by except

3.     a start element isno longer allowed to have a name attribute

4.     an attribute element isno longer allowed to have a global attribute

References

Compatibility

James Clark,Makoto MURATA, editors. RELAXNG DTD Compatibility. OASIS, 2001.

RELAX

MURATAMakoto. RELAX(Regular Language description for XML). INSTAC(Information Technology Research and Standardization Center), 2001.

RELAX NG

James Clark,Makoto MURATA, editors. RELAX NG Specification. OASIS, 2001.

TREX

James Clark. TREX - Tree RegularExpressions for XML. Thai Open Source Software Center, 2001.

W3C XML Schema Datatypes

Paul V. Biron,Ashok Malhotra, editors. XML Schema Part 2: Datatypes. W3C (World WideWeb Consortium), 2001.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值