# Validation using RELAX NG

4Suite has RELAX NG support based on a bundling of Eric van der Vlists's XVIF implementation.

First of all, you can use the 4xml command line for RELAX NG validation with the --rng flag. For instance, take the following RELAX NG schema (rng-tut3.rng):

    <element name="addressBook" xmlns="[http://relaxng.org/ns/structure/1.0][13]">
      <zeroOrMore>
        <element name="card">
          <element name="name">
            <text/>
          </element>
          <element name="email">
            <text/>
          </element>
        </element>
      </zeroOrMore>
    </element>

The following document (rng-tut1.xml) is valid against the schema:

    <addressBook>
      <card>
        <name>John Smith</name>
        <email>js@example.com</email>
      </card>
      <card>
        <name>Fred Bloggs</name>
        <email>fb@example.net</email>
      </card>
    </addressBook>

As you can check as follows:

    $ 4xml --rng=rng-tut3.rng rng-tut1.xml
    <?xml version="1.0" encoding="utf-8"?>
    <addressBook>
      <card>
        <name>John Smith</name>
        <email>js@example.com</email>
      </card>
      <card>
        <name>Fred Bloggs</name>
        <email>fb@example.net</email>
      </card>
    </addressBook>

Since it passes the schema, 4xml continues normal operation, re-serializing the XML back to stdout.

The following document (rng-tut7.xml) is not valid against the schema:

    <addressBook>i
      <card>
        <givenName>John</givenName>
        <familyName>Smith</familyName>
      <email>js@example.com</email>
      </card>
      <card>
        <name>Fred Bloggs</name>
        <email>fb@example.net</email>
      </card>
    </addressBook>

Which you can check as follows:

    $ 4xml --rng=rng-tut7.rng rng-tut1.xml
    Traceback (most recent call last):
      File "/home/uogbuji/lib/python2.2/site-packages/Ft/Share/Bin/4xml", line 5, in ?
        XmlCommandLineApp().run()
      File "/home/uogbuji/lib/python2.2/site-packages/Ft/Lib/CommandLine/CommandLineApp.py", line 90, in run
        cmd.run_command(self.authenticationFunction)
      File "/home/uogbuji/lib/python2.2/site-packages/Ft/Lib/CommandLine/Command.py", line 83, in run_command
        self.function(self.clOptions, self.clArguments)
      File "/home/uogbuji/lib/python2.2/site-packages/Ft/Xml/_4xml.py", line 89, in Run
        raise RngInvalid(result)
    Ft.Xml.Xvif.RngInvalid: _Pattern Empty, no content expected,
    node <cElement at 0x838d7f4: name u'card', 0 attributes, 7 children>

The exception is for the invalid pattern.

You can also access validation through the Python API using the new Ft.Xml.Xvif.RelaxNgValidator class. For example:

    from Ft.Xml.Xvif import RelaxNgValidator
    from Ft.Xml import InputSource
    from Ft.Lib import Uri
    factory = InputSource.DefaultFactory
    rng_uri = Uri.OsPathToUri("rng-tut3.rng", attemptAbsolute=1)
    src_uri = Uri.OsPathToUri("rng-tut1.xml", attemptAbsolute=1)
    rng_isrc = factory.fromUri(rng_uri)
    src_isrc = factory.fromUri(src_uri)

    validator = RelaxNgValidator(rng_isrc)
    result = validator.isValid(src_isrc)
    if result:
        print "Valid"
    else:
        print "Invalid"

The isValid() method returns a 1 or 0 for validity. To get the actual structure returned by the validator, use the validate() method instead. This structure can easily be turned into an exception object. The following variation prints "Valid" if valid, and raises an exception if not:

    from Ft.Xml.Xvif import RelaxNgValidator, RngInvalid
    from Ft.Xml import InputSource
    factory = InputSource.DefaultFactory
    from Ft.Lib import Uri
    factory = InputSource.DefaultFactory
    rng_uri = Uri.OsPathToUri("rng-tut3.rng", attemptAbsolute=1)
    src_uri = Uri.OsPathToUri("rng-tut1.xml", attemptAbsolute=1)
    rng_isrc = factory.fromUri(rng_uri)
    src_isrc = factory.fromUri(src_uri)

    validator = RelaxNgValidator(rng_isrc)
    result = validator.validate(src_isrc)
    if result.nullable():
        print "Valid"
    else:
        raise RngInvalid(result)

If you want to use the validation error message without raising an exception:

    # Set-up as above
    result = validator.validate(src_isrc)
    if result.nullable():
        print "Valid"
    else:
        print result.msg