Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can relaxng specify an unordered set of elements with the same name, but different attributes?

Tags:

xml

relaxng

Im working to automate the testing of an API which takes and returns XML, so I want to translate the documented return data of the API into schema as much as possible. I chose RelaxNG for this task based on ease of use and learning.

Before I throw in all the info, here's the question:

Is it possible to describe "unordered set of elements, with the same name but different attributes" ?

Here is a sample object for what I'm having trouble describing:

<item>
    <id>d395136e-d060-4a6e-887c-c0337dd7ad09</id>
    <name>The item has a name</name>
    <link rel="self" type="type1" href="url" />
    <link rel="download" type="type2" href="url" />
    <link rel="relatedData" type="type3" href="url" />
</item>

The link objects are the bit that I'm getting hung up on. Here is the problem:

  • The order of elements inside item is not guaranteed, so I am trying to put all elements in <interleave> structure.
  • There will be multiple <link> elements inside <item>, with different sets of attributes (ie, <item> MUST have a 'self' link, a 'download' link, and a 'relatedData' link to be valid).
  • One of each link type is required, but again order is not guaranteed.

I tried to describe the schema like so:

<element name="item">
    <interleave>
        <element name="id"><text/></element>
        <element name="name"><text/></element>
        <ref name="selfLink"/>
        <ref name="launchLink"/>
        <ref name="thumbnailLink"/>
    </interleave>
</element>

the 'link' references are defined elsewhere like so:

 <define name="selfLink">
 <element name="link">
     <attribute name="href"><text/></attribute>
     <attribute name="rel"><value>self</value></attribute>
     <attribute name="type"><value>type1</value></attribute>
 </element>
 </define>

The parser is not pleased about this - from jing I get error: the element "link" can occur in more than one operand of "interleave". I can see what its getting at but I hoped it could handle the idea of 'elements with the same name but different attributes' as unique items.

Moving the link refs out of interleave gets it to parse, but I'll be waiting for the validator to blow up whenever the order changes in the returned data.

Any ideas, or is this impossible? Is there an inherent issue with the XML I am processing that will require me to move some of this up to higher processing logic in my test application (manually check for each link type after running a more generic XML validation?)

like image 206
James Avatar asked Oct 07 '22 01:10

James


2 Answers

It looks like you have stumbled upon a restriction on interleave in RELAX NG. I would try to do this in Schematron, or perhaps a combination of RELAX NG and Schematron.

Here is a snippet that checks your <link> elements using the version of Schematron that is supported by Jing:

<schema xmlns="http://www.ascc.net/xml/schematron">
  <pattern name="link pattern">
    <rule context="item">
      <assert test='count(link) = 3'>There must be 3 link elements.</assert>
      <assert test="count(link[@rel = 'self' and @type ='type1']) = 1">There must be 1 link element wwhere @rel='self' and @type='type1'.</assert>
      <assert test="count(link[@rel = 'download' and @type ='type2']) = 1">There must be 1 link element where @rel='download' and @type='type2'.</assert>
      <assert test="count(link[@rel = 'relatedData' and @type = 'type3']) = 1">There must be 1 link element where @rel='relatedData' and @type='type3'.</assert>
    </rule>
  </pattern>
</schema>
like image 79
mzjn Avatar answered Oct 13 '22 11:10

mzjn


See if the following schema helps

<grammar xmlns="http://relaxng.org/ns/structure/1.0">
<start>
    <element name="item">
        <interleave>
            <element name="id"><text/>
            </element>
            <element name="name"><text/></element>
            <oneOrMore>
                <ref name="link"/>
            </oneOrMore>
        </interleave>
    </element>
</start>

<define name="link">
    <element name="link">
        <attribute name="href"/>
        <choice>
            <group>
                <attribute name="rel"><value>self</value></attribute>
                <attribute name="type"><value>type1</value></attribute>
            </group>
            <group>
                <attribute name="rel"><value>download</value></attribute>
                <attribute name="type"><value>type2</value></attribute>
            </group>
            <group>
                <attribute name="rel"><value>relatedData</value></attribute>
                <attribute name="type"><value>type3</value></attribute>
            </group>
        </choice>
    </element>
</define>
</grammar>
like image 36
Ivan Pedruzzi Avatar answered Oct 13 '22 09:10

Ivan Pedruzzi