1

I'm setting up Schema for our xml input/output, and have run into an issue where XMLSpy validates ok, but Xerces fails on one of the xs:asserts. I'm using the latest xerces, xerces-2_12_0-xml-schema-1.1.

I have included all the .jar files from that distribution (except the xercesSamples.jar)

The test code is:

SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/XML/XMLSchema/v1.1");
factory.setFeature("http://apache.org/xml/features/validation/cta-full-xpath-checking", true);
Schema schema = factory.newSchema(new File("C:/Imports/Test.xsd"));
validator = schema.newValidator();
validator.validate(new StreamSource("C:/Imports/Test.xml"));

I've trimmed the xsd file down to this:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:lit="http://www.w3schools.com" xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" targetNamespace="http://www.w3schools.com" elementFormDefault="qualified" attributeFormDefault="unqualified" vc:minVersion="1.1">
    <xs:element name="MetrixXML">
        <xs:complexType>
            <xs:all>
                <xs:element ref="lit:Page" minOccurs="1" maxOccurs="unbounded"/>
            </xs:all>
            <xs:attribute name="SchemaVersion" type="xs:float" use="required"/>
        </xs:complexType>
    </xs:element>
    <xs:element name="Page">
        <xs:complexType>
            <xs:attribute name="ContentPositionRule" type="xs:string"/>
            <xs:attribute name="FilePageNum" type="xs:nonNegativeInteger"/>
            <xs:assert test="(//@SchemaVersion ge 2.1) or ((//@SchemaVersion lt 2.1) and not (@ContentPositionRule))"/>
        </xs:complexType>
    </xs:element>
</xs:schema>

The xml is:

<?xml version="1.0" encoding="UTF-8"?>
<MetrixXML xmlns="http://www.w3schools.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.w3schools.com Test.xsd" SchemaVersion="2.1" >
    <Page FilePageNum="1"/>
    <Page ContentPositionRule="CenterEachPage"/>
</MetrixXML>

The error I get is:

org.xml.sax.SAXParseException: cvc-assertion: Assertion evaluation ('(//@SchemaVersion ge 2.1) or ((//@SchemaVersion lt 2.1) and not (@ContentPositionRule))') for element 'Page' on schema type '#AnonType_Page' did not succeed.

In XMLSpy, if I set SchemaVersion to 2.0, the assert fails. If I set it to 2.1, the assert succeeds.

Is there some Feature flag that I need to set?

Update: Apparently XMLSpy is allowing things it shouldn't allow.

So, the desired test is that if (SchemaVersion < 2.1) AND any element contains a "ContentPositionRule" attribute THEN it should fail.

CasaDelGato
  • 603
  • 7
  • 17
  • Note that the "SchemaVersion" attribute has nothing to do with xsd. It's a private attribute of our xml. I had already read the referenced issue, and it is NOT the same, as that one is having problems with xsd 1.1, and mine handles xsd 1.1 - but xerces isn't handling more complex assert statements. The schema factory is being told to use xsd 1.1, and does. It understands the assert - but doesn't seem to be evaluating it correctly. – CasaDelGato Sep 19 '19 at 20:10

1 Answers1

1

Move the assertion up a level in the hierarchy and ensure that it references only descendents of the associated element:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:lit="http://www.w3schools.com"
           xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
           targetNamespace="http://www.w3schools.com" 
           elementFormDefault="qualified"
           attributeFormDefault="unqualified" vc:minVersion="1.1">
  <xs:element name="MetrixXML">
    <xs:complexType>
      <xs:all>
        <xs:element ref="lit:Page" minOccurs="1" maxOccurs="unbounded"/>
      </xs:all>
      <xs:attribute name="SchemaVersion" type="xs:float" use="required"/>
      <xs:assert test=" (@SchemaVersion ge 2.1) or 
                       ((@SchemaVersion lt 2.1) and 
                         not (lit:Page/@ContentPositionRule))       
</xs:complexType>
  </xs:element>
  <xs:element name="Page">
    <xs:complexType>
      <xs:attribute name="ContentPositionRule" type="xs:string"/>
      <xs:attribute name="FilePageNum" type="xs:nonNegativeInteger"/>
    </xs:complexType>
  </xs:element>
</xs:schema>

An assertion is only allowed to reference the element on which it appears and that element's descendents – not its ancestors, siblings, etc.

See also:

XMLSpy's observed behavior

Although it's technically (albeit unhelpfully) conformant to provide no diagnostic assistance for assertions over siblings or ancestors of the element on which an assertion appears, XMLSpy should not be reporting differing validation results depending upon sibling or ancestor state.

W3C XML Schema Definition Language (XSD) 1.1 Part 1: Structures

Validation Rule: Assertion Satisfied

[...]

1.3 From the "partial" ·post-schema-validation infoset·, a data model instance is constructed as described in [XDM]. The root node of the [XDM] instance is constructed from E; the data model instance contains only that node and nodes constructed from the [attributes], [children], and descendants of E. Note: It is a consequence of this construction that attempts to refer, in an assertion, to the siblings or ancestors of E, or to any part of the input document outside of E itself, will be unsuccessful. Such attempted references are not in themselves errors, but the data model instance used to evaluate them does not include any representation of any parts of the document outside of E, so they cannot be referred to.

Note: It is a consequence of this construction that attempts to refer, in an assertion, to the siblings or ancestors of E, or to any part of the input document outside of E itself, will be unsuccessful. Such attempted references are not in themselves errors, but the data model instance used to evaluate them does not include any representation of any parts of the document outside of E, so they cannot be referred to.

[Emphasis added.]

kjhughes
  • 106,133
  • 27
  • 181
  • 240
  • So, you are saying that the "//" search from root is not SUPPOSED to work in an assert? Also note that moving the assert up - means that it doesn't have the ContentPositionRule attribute to check, so the assert always succeeds. This is also an extremely simplified example, as in the real schema, the element appears in several different places in the hierarchy. Which means that I would need to add another copy of the assert for each possible path. This makes the asserts FAR FAR less useful. – CasaDelGato Sep 19 '19 at 20:55
  • No, I'm saying that assertions should *not* attempt to reference sibling or ancestor nodes of the associated element. You can access descendents relatively via `.//`, however, so if I understand your goal correctly, this shouldn't be as prohibitive as you may have feared. – kjhughes Sep 19 '19 at 21:02
  • Just spent some time trying variations. This appears to work when in the top level element; ... – CasaDelGato Sep 19 '19 at 21:09
  • So, pretty much ALL my between element validations will need to be in the top level element. Not especially good for maintainability. – CasaDelGato Sep 19 '19 at 21:11
  • Spoke too soon. I can't seem to find a syntax variation that allows me to limit the check for ContentPositionRule to just the "Page" elements under the top element. – CasaDelGato Sep 19 '19 at 21:13
  • Maintainability: To make assertions over the domain of the entire document tree, it's not unreasonable to position such assertions at the top of the hierarchy. Global assertions do, after all, pertain to global scope. – kjhughes Sep 19 '19 at 21:14
  • To limit your `@ContentPositionRule` assertions to only `lit:Page` elements, use `.//lit:Page[@ContentPositionRule = 'something']` to test for a value or just `.//lit:Page[@ContentPositionRule]` to test for the attribute existence. – kjhughes Sep 19 '19 at 21:16
  • Already tried that, and multiple variations. Using my example xml, it always passes (true), even if the SchemaVersion is set to 2.0. – CasaDelGato Sep 19 '19 at 21:17
  • The only variation that seems to work is .//@ContentPositionRule, and that doesn't limit it to just the Page elements, which won't be a valid test, as that attribute is allowed at all times in some other elements. – CasaDelGato Sep 19 '19 at 21:22
  • Edit your question and state *clearly and logically in natural language* your exact assertion requirements. Trying to discern your XPath assertion needs based upon a series of provided (possibly flawed) XPaths is too hard. ;-) – kjhughes Sep 19 '19 at 21:24
  • And thank you. the "(.//lit:Page[@ContentPositionRule])" seems to work. Is there a decent human-readable reference for the assert/xpath stuff somewhere? So far I've been working off nearly nothing in docs. – CasaDelGato Sep 19 '19 at 21:46
  • Glad you got it working! For XPath, I'd recommend the [XPath 1.0 spec](https://www.w3.org/TR/1999/REC-xpath-19991116/), which is small and very approachable. See especially the example XPaths given in the intro to section [2 Location Paths](https://www.w3.org/TR/1999/REC-xpath-19991116/#location-paths) and [2 Abbreviated Syntax](https://www.w3.org/TR/1999/REC-xpath-19991116/#location-paths). Anything online or in book form by Michael Kay is great too. – kjhughes Sep 19 '19 at 22:25