Search code examples
xsdschemarelaxngrelaxng-compact

Control attribute values order in RelaxNG


Is it possible to control the order of the attribute values in Relax NG? which can be achieved using xs:assert in schema?

XML:

<body>
 <h1 class="title">title</h1>
 <h2 class="subtitle">subtitle</h2>
 <p class="paragraph1">para text 1</p>
 <p class="paragraph2">Para text 2</p>
 <p class="paragraph3">Para text 2</p>
 </body>

The class value should be in order, paragraph1 should always come first and paragraph2 should come after paragraph1. The assert I tried in schema:

<xs:assert test="p[1]/@class = 'paragraph1'
and ((every $i in p[2] satisfies $i/@class = 'paragraph2')
and (every $i in p[3] satisfies $i/@class = 'paragraph3'))  "/>

Solution

  • A (compact-syntax) RelaxNG grammar to express what the question describes could be written as:

    start = element body { h1?, h2?, p.paragraph1?, p.paragraph2?, p.paragraph3? }
    h1 = element h1 { text & attribute class { string } }
    h2 = element h2 { text & attribute class { string } }
    p.paragraph1 = element p { text & attribute class { string "paragraph1" } }
    p.paragraph2 = element p { text & attribute class { string "paragraph2" } }
    p.paragraph3 = element p { text & attribute class { string "paragraph3" } }
    

    Expressed in the RelaxNG XML syntax:

    <grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="">
      <start>
        <element name="body">
          <optional>
            <ref name="h1"/>
          </optional>
          <optional>
            <ref name="h2"/>
          </optional>
          <optional>
            <ref name="p.paragraph1"/>
          </optional>
          <optional>
            <ref name="p.paragraph2"/>
          </optional>
          <optional>
            <ref name="p.paragraph3"/>
          </optional>
        </element>
      </start>
      <define name="h1">
        <element name="h1">
          <interleave>
            <text/>
            <attribute name="class">
              <data type="string"/>
            </attribute>
          </interleave>
        </element>
      </define>
      <define name="h2">
        <element name="h2">
          <interleave>
            <text/>
            <attribute name="class">
              <data type="string"/>
            </attribute>
          </interleave>
        </element>
      </define>
      <define name="p.paragraph1">
        <element name="p">
          <interleave>
            <text/>
            <attribute name="class">
              <value type="string">paragraph1</value>
            </attribute>
          </interleave>
        </element>
      </define>
      <define name="p.paragraph2">
        <element name="p">
          <interleave>
            <text/>
            <attribute name="class">
              <value type="string">paragraph2</value>
            </attribute>
          </interleave>
        </element>
      </define>
      <define name="p.paragraph3">
        <element name="p">
          <interleave>
            <text/>
            <attribute name="class">
              <value type="string">paragraph3</value>
            </attribute>
          </interleave>
        </element>
      </define>
    </grammar>