Search code examples
xmlrelaxngrelaxng-compact

RELAX NG conditional datatype based on other element


My goal is to have a RELAX NG compact schema which enforces that an element's datatype matches that of the parent element's datatype.

I currently have the following schema:

start = Tickmarks

Tickmarks = element tickmarks { 
    attribute from { xsd:float | xsd:date },
    attribute to { xsd:float | xsd:date },
    Tick*
}

Tick = element tick { 
    attribute value { xsd:float | xsd:date },
    attribute label { text }
}

What I would like is a schema that describes a document where by if Tickmarks/@from is a float, Tickmarks/Tick/@value must also be a float.

So essentially it should satisfy the following tests:

VALID

<tickmarks from="1" to="10">
  <tick value="3" label="3"/>
  <tick value="7" label="7"/>
</tickmarks>

VALID

<tickmarks from="2000-01-01" to="2006-01-01">
  <tick value="2002-01-01" label="2002"/>
  <tick value="2004-01-01" label="2004"/>
</tickmarks>

INVALID

<tickmarks from="2000-01-01" to="2006-01-01">
  <tick value="10" label="10"/>
  <tick value="20" label="20"/>
</tickmarks>

INVALID

<tickmarks from="1" to="10">
  <tick value="2002-01-01" label="2002"/>
  <tick value="2004-01-01" label="2004"/>
</tickmarks>

Is this possible with RELAX NG compact?


Solution

  • If you want to stay with pure RELAX NG (and avoid alternative solutions like embedded Schematron rules), you could use:

    start = Tickmarks
    
    Tickmarks = element tickmarks { 
        (
         attribute from { xsd:float },
         attribute to { xsd:float },
         Tick-float*)
        | (
           attribute from { xsd:date },
           attribute to { xsd:date },
           Tick-date*)
    }
    
    Tick-float = element tick { 
        attribute value { xsd:float },
        attribute label { text }
    }
    
    Tick-date = element tick { 
        attribute value { xsd:date },
        attribute label { text }
    }