About using xs:unique on xsi:type in a sequence

前端 未结 2 1922
时光取名叫无心
时光取名叫无心 2021-01-23 17:49

It is possible to have a unique constraint on an element\'s type ?

Let\'s say I have a Noah\'s Ark where Animal/@name must be unique.

Below\'s an XML that do not

相关标签:
2条回答
  • 2021-01-23 17:55

    All the implementations I could test on XSD 1.0 seem to agree on one thing here: the identity constraints will be tested only if the selectors/fields match XML nodes for which an associated user defined schema component can be found. xsi:type, while it is a schema related markup, is not considered here since its intention is different. It might prove academically interesting to check if a Schematron assertion might help here...

    UPDATE: I am including the Schematron version, too; I am running the ISO version, XSLT1 w/ Microsoft extensions since I am on .NET.

    <?xml version="1.0"?>
    <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" xmlns:ms="urn:schemas-microsoft-com:xslt">
        <sch:ns uri="http://www.w3.org/2001/XMLSchema-instance" prefix="xsi"/>
        <sch:ns uri="urn:schemas-microsoft-com:xslt" prefix="ms"/>
    
       <sch:pattern id="about-using-xsunique-on-xsitype-in-a-sequence">
          <sch:rule context="//Animal/@xsi:type">
            <sch:let name="targetNodeSet" value="//Animal/@xsi:type"/>
            <sch:assert test="count($targetNodeSet[concat('{', ms:namespace-uri(.), '}', ms:local-name(.)) = concat('{', ms:namespace-uri(current()), '}', ms:local-name(current()))]) = 1">
                Only one-of-a-kind animal is allowed.</sch:assert>
            <sch:assert test="count($targetNodeSet[. = current()]) = 1">
                Only one-of-a-kind animal is allowed (naive).</sch:assert>
          </sch:rule>
       </sch:pattern>
    </sch:schema>
    

    I've defined two assertion, a "naive" one that textually compares the value of the xsi:type attribute; and the "correct" one which compares them as QName s.

    For this XML:

    <ns:NoahsArk xmlns:ns1="http://www.xxx.com" xmlns:ns="http://www.xxx.com" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:schemaLocation="http://www.xxx.com Persons.xsd">  
        <Animal xs:type="ns:Dog" ns:name="A" ns:pedigree="caniche"/> 
        <Animal xs:type="ns1:Dog" ns:name="B" ns:pedigree="caniche"/> 
        <Animal xs:type="ns1:Dog" ns:name="C" ns:pedigree="caniche"/> 
    </ns:NoahsArk> 
    

    This is the result:

    <?xml version="1.0" encoding="utf-8" standalone="yes"?>
    <svrl:schematron-output title="" schemaVersion="" xmlns:svrl="http://purl.oclc.org/dsdl/svrl" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:schold="http://www.ascc.net/xml/schematron" xmlns:sch="http://www.ascc.net/xml/schematron" xmlns:iso="http://purl.oclc.org/dsdl/schematron" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ms="urn:schemas-microsoft-com:xslt">
        <svrl:ns-prefix-in-attribute-values uri="http://www.w3.org/2001/XMLSchema-instance" prefix="xsi"/>
        <svrl:ns-prefix-in-attribute-values uri="urn:schemas-microsoft-com:xslt" prefix="ms"/>
        <svrl:active-pattern id="about-using-xsunique-on-xsitype-in-a-sequence" name="about-using-xsunique-on-xsitype-in-a-sequence"/>
        <svrl:fired-rule context="//Animal/@xsi:type"/>
        <svrl:failed-assert test="count($targetNodeSet[concat(', ms:namespace-uri(.), ', ms:local-name(.)) = concat(', ms:namespace-uri(current()), ', ms:local-name(current()))]) = 1" location="/@*[local-name()='type' and namespace-uri()='http://www.w3.org/2001/XMLSchema-instance']">
            <svrl:text>
                Only one-of-a-kind animal is allowed.</svrl:text>
        </svrl:failed-assert>
        <svrl:fired-rule context="//Animal/@xsi:type"/>
        <svrl:failed-assert test="count($targetNodeSet[concat(', ms:namespace-uri(.), ', ms:local-name(.)) = concat(', ms:namespace-uri(current()), ', ms:local-name(current()))]) = 1" location="/@*[local-name()='type' and namespace-uri()='http://www.w3.org/2001/XMLSchema-instance']">
            <svrl:text>
                Only one-of-a-kind animal is allowed.</svrl:text>
        </svrl:failed-assert>
        <svrl:failed-assert test="count($targetNodeSet[. = current()]) = 1" location="/@*[local-name()='type' and namespace-uri()='http://www.w3.org/2001/XMLSchema-instance']">
            <svrl:text>
                Only one-of-a-kind animal is allowed (naive).</svrl:text>
        </svrl:failed-assert>
        <svrl:fired-rule context="//Animal/@xsi:type"/>
        <svrl:failed-assert test="count($targetNodeSet[concat(', ms:namespace-uri(.), ', ms:local-name(.)) = concat(', ms:namespace-uri(current()), ', ms:local-name(current()))]) = 1" location="/@*[local-name()='type' and namespace-uri()='http://www.w3.org/2001/XMLSchema-instance']">
            <svrl:text>
                Only one-of-a-kind animal is allowed.</svrl:text>
        </svrl:failed-assert>
        <svrl:failed-assert test="count($targetNodeSet[. = current()]) = 1" location="/@*[local-name()='type' and namespace-uri()='http://www.w3.org/2001/XMLSchema-instance']">
            <svrl:text>
                Only one-of-a-kind animal is allowed (naive).</svrl:text>
        </svrl:failed-assert>
    </svrl:schematron-output>
    

    As you can see from the failed assertions, the "naive" way is flagging only 2 of 3 instances; again, comparing the value of the xsi:type attribute as text is not the same as comparing QName s (which is what @xsi:types are).

    0 讨论(0)
  • 2021-01-23 18:08

    Thanks for your response.

    Just for fun I made an attempt with Schematron ; however I couldn't make it work since the XPath expression I find out uses "current()" which is not available in oxygen or XMLSpy, so I can't test it :(

        <?xml version="1.0" encoding="UTF-8"?>
        <schema xmlns="http://www.ascc.net/xml/schematron" xmlns:ns="http://www.xxx.com" >
            <ns prefix="ns" uri="http://www.xxx.com"/>
            <ns prefix="xs" uri="http://www.w3.org/2001/XMLSchema-instance"/>
                <pattern name="AnimalNameUnicity">
                    <rule context="Animals/Animal">
                        <assert test="count(//@ns:name[ . = current()]) > 1">name not unique !</assert>        
                    </rule>
                </pattern> 
        </schema>
    
    0 讨论(0)
提交回复
热议问题