-->

Dynamic XML Schema Validates Subsection of Documen

2019-06-08 02:50发布

问题:

I have a situation similar to "XSD with elements from other namespace", where I have 2 peices of XML.

One peice of XML acts as a wrapper to the other. In a simple example - we have a single MESSAGES wrapper, containing at least one MESSAGE elements. Each MESSAGE element contains a PAYLOAD which is validated against an XSD.

Each PAYLOAD in a given MESSAGES parent will validate against the same XSD. However, different MESSAGES can contain PAYLOADs that validate against a whole host of XSDs. I literally have hundreds of XSDs to validate different types of PAYLOAD.

I want to have my own XSD that validates the structure of the MESSAGES wrapper, and then handsover responsibility of validating the PAYLOAD to one of the many stock XSDs I have, depending on PAYLOAD type.

The important point is we cannot import the PAYLOAD XSD into the MESSAGE XSD - because the PAYLOAD schema type is not fixed at XSD-level. But it is fixed at XML-level.

<MESSAGES>
    <MESSAGE>
         <RECIPIENT>Foo</RECIPIENT>
         <PAYLOAD>
              <!-- Large message containing many possible elements -->
         </PAYLOAD>
    <MESSAGE>
    <MESSAGE>
         <RECIPIENT>Bar</RECIPIENT>
         <PAYLOAD>
              <!-- Large message containing many possible elements -->
         </PAYLOAD>
    <MESSAGE>
</MESSAGES>

Two things I've noticed so far. If I provide no schema at the parent MESSAGES level, I can provide a schema at PAYLOAD level, and this will correctly validate the PAYLOAD. However there is no validation of the wrapper then.

<MESSAGES>
     <MESSAGE>
         <RECIPIENT>Foo</RECIPIENT>
         <PAYLOAD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xmlns="http://www.payload.org"
                  xsi:schemaLocation="http://www.payload.org xsd/payload-type-a.xsd">
              <!-- Large message containing many possible elements -->
         </PAYLOAD>
    <MESSAGE>
</MESSAGES>

I can also do the reverse, i.e. validate the MESSAGES wrapper, but ignore the PAYLOAD, using the following XSD on the whole of XML:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="http://www.message.org">
     <xs:element name="MESSAGES">
         <xs:complexType>
             <xs:sequence>
                 <xs:element name="MESSAGE" maxOccurs="unbounded">
                     <xs:complexType>
                         <xs:sequence>
                             <xs:element name="RECIPIENT" type="xs:string"/>
                             <xs:any processContents="skip"/>
                         </xs:sequence>
                    </xs:complexType>
                 </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

With XML:

<MESSAGES xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns="http://www.messages.org"
          xsi:schemaLocation="http://www.messages.org xsd/messages.xsd">
    <MESSAGE>
         <RECIPIENT>Foo</RECIPIENT>
         <PAYLOAD>
              <!-- Large message containing many possible elements -->
         </PAYLOAD>
    <MESSAGE>
</MESSAGES>

But what I can't do is combine both XSDs inside the XML:

<MESSAGES xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns="http://www.messages.org"
          xsi:schemaLocation="http://www.messages.org xsd/messages.xsd">
    <MESSAGE>
         <RECIPIENT>Foo</RECIPIENT>
         <PAYLOAD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xmlns="http://www.payload.org"
                  xsi:schemaLocation="http://www.payload.org xsd/payload-type-a.xsd">
              <!-- Large message containing many possible elements -->
         </PAYLOAD>
    <MESSAGE>
</MESSAGES>

I'm newish to XSD, so I can kinda see that what I'm trying to do is perhaps not in the spirit of XML/Validation, but it does strike me as a reasonable thing to want to do - I'd be suprised if I'm the first person who has come-up against this!

The only theoretical solution that I can dream-up is to have the XSDs themselves dynamically generated by the XSLT that generates the MESSAGES. That is, have the XSLT (omitted for brevity), output both the XML and the Schema to validate it. Then I could use the XSD import statement, as there is a 1-to-1 mapping of XSLT document to PAYLOAD type.

This will significantly complicate things however!

I'm hoping there is a way of validating different parts of an XML file with different Schemas, and controlling that from the XML file itself.

Any ideas greatly appreicated!

回答1:

I got this working in the end. The reality is that I think I was doing a few little things wrong, that meant I went around in circless. Eventually I was able to produce the following - the only thing missing was elementFormDefault="qualified" in the xs:schema atributes, and that the processContents should be strict if the schemas are validating correctly.

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="http://www.message.org"
           elementFormDefault="qualified">
     <xs:element name="MESSAGES">
         <xs:complexType>
             <xs:sequence>
                 <xs:element name="MESSAGE" maxOccurs="unbounded">
                     <xs:complexType>
                         <xs:sequence>
                             <xs:element name="RECIPIENT" type="xs:string"/>
                             <xs:any processContents="strict"/>
                         </xs:sequence>
                    </xs:complexType>
                 </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

Armed with this XSD I was able to get the following XML to work:

<MESSAGES xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns="http://www.messages.org"
          xsi:schemaLocation="http://www.messages.org xsd/messages.xsd">
    <MESSAGE>
         <RECIPIENT>Foo</RECIPIENT>
         <PAYLOAD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xmlns="http://www.payload.org"
                  xsi:schemaLocation="http://www.payload.org xsd/payload-type-a.xsd">
              <!-- Large message containing many possible elements -->
         </PAYLOAD>
    <MESSAGE>
</MESSAGES>

Finally it's worth noting that you can add multiple schemas at the top of the XML and then select them by altering the xmlns just like the above example:

<MESSAGES xsi:schemaLocation="http://www.messages.org xsd/messages.xsd
                              http://www.payload.org xsd/payload.xsd>"