Project

Profile

Help

SaxonCS: Null reference exception compiling a schema importing another schema that targets the xpath-functions namespace

Added by Jeff Tennessen about 3 years ago

I'm excited about SaxonCS and am evaluating it for our organization. There's one error I've run into that I haven't been able to figure out. (I've pared these examples down to the minimum necessary to reproduce the behavior -- at least in my environment -- so I realize they look a little silly.)

If I have a schema, A.xsd, whose target namespace is the xpath-functions ns:

<?xml version="1.0" encoding="utf-8"?>
<xsd:schema version="1.1"
            targetNamespace="http://www.w3.org/2005/xpath-functions"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema">
</xsd:schema>

and I compile A.xsd using this C# code:

Processor proc = new Processor(true);
proc.SetProperty("http://saxon.sf.net/feature/timing", "true");
proc.SetProperty("http://saxon.sf.net/feature/validation-warnings", "false");
List<Error> compileErr = new();
SchemaManager mgr = proc.SchemaManager;
mgr.XsdVersion = "1.1";
mgr.ErrorReporter = (e) => compileErr.Add(e);
mgr.Compile(new Uri("A.xsd"));

it compiles fine; however, if A.xsd is imported by B.xsd:

<?xml version="1.0" encoding="utf-8"?>
<xsd:schema version="1.1"
            targetNamespace="http://tempuri.org/b"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:import schemaLocation="A.xsd" namespace="http://www.w3.org/2005/xpath-functions" />
</xsd:schema>

and I attempt to compile B.xsd using the same C#, I get a null reference exception with this stack trace:

   at Saxon.Eej.ee.schema.sdoc.XSDImport.prepareAttributes()
   at Saxon.Eej.ee.schema.sdoc.SchemaElement.processAllAttributes()
   at Saxon.Eej.ee.schema.sdoc.SchemaElement.processAllAttributes()
   at Saxon.Eej.ee.schema.sdoc.SchemaReader.read(Source source, SchemaCompiler compiler, PipelineConfiguration pipe, SchemaElement referrer)
   at Saxon.Eej.config.EnterpriseConfiguration.addSchemaSource(Source schemaSource, ErrorReporter errorReporter, SchemaCompiler compiler)
   at Saxon.Eej.config.EnterpriseConfiguration.addSchemaSource(Source schemaSource, ErrorReporter errorReporter)
   at Saxon.Eej.ee.s9api.SchemaManagerImpl.load(Source source)
   at Saxon.Api.SchemaManager.Compile(Uri uri)
   at <my code>

On the other hand, if the target namespace used by A.xsd is something else (I've tried several, http://tempuri.org/a works), B.xsd compiles fine. Our current production solution uses Saxon-EE 9.6.0.6N, and we haven't encountered this behavior there.

Am I doing something incorrectly with v11? Thanks!


Replies (7)

Please register to reply

RE: SaxonCS: Null reference exception compiling a schema importing another schema that targets the xpath-functions namespace - Added by Michael Kay about 3 years ago

Well, a Null Reference Exception certainly implies a bug.

However, defining your own schema for the xpath-functions namespace is definitely not recommended. That namespace is "owned" by W3C, and W3C has already defined a schema for that namespace, and Saxon has built-in knowledge of this schema, so it's entitled to ignore the schemaLocation in your xs:import and use the schema it already knows about.

RE: SaxonCS: Null reference exception compiling a schema importing another schema that targets the xpath-functions namespace - Added by Jeff Tennessen about 3 years ago

We use that target namespace to validate XML converted from JSON via XSLT 3.0. Here's a slightly more fleshed-out (though still contrived) example:

JSON:

{
  "foo": "one",
  "bar": 2
}

Converted XML:

<?xml version="1.0" encoding="utf-8"?>
<map xmlns="http://www.w3.org/2005/xpath-functions">
  <string key="foo">one</string>
  <number key="bar">2</number>
</map>

Schema:

<?xml version="1.0" encoding="utf-8"?>
<xsd:schema version="1.1"
            targetNamespace="http://www.w3.org/2005/xpath-functions"
            xmlns:tns="http://www.w3.org/2005/xpath-functions"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            elementFormDefault="qualified">
  <xsd:element name="map">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="string" minOccurs="1" maxOccurs="1">
          <xsd:complexType>
            <xsd:simpleContent>
              <xsd:extension base="xsd:string">
                <xsd:attribute name="key" use="required" fixed="foo" />
              </xsd:extension>
            </xsd:simpleContent>
          </xsd:complexType>
        </xsd:element>
        <xsd:element name="number" minOccurs="1" maxOccurs="1">
          <xsd:complexType>
            <xsd:simpleContent>
              <xsd:extension base="xsd:integer">
                <xsd:attribute name="key" use="required" fixed="bar" />
              </xsd:extension>
            </xsd:simpleContent>
          </xsd:complexType>
        </xsd:element>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

If we're doing this incorrectly, I would be grateful if you could point me in the right direction. Thanks!

RE: SaxonCS: Null reference exception compiling a schema importing another schema that targets the xpath-functions namespace - Added by Michael Kay about 3 years ago

Tricky one. The XSD architecture does tend to assume that there is exactly one schema for every namespace, and that if a processor already knows the schema for a particular namespace then it can ignore any schemaLocation you supply, and use the schema it already knows about. That makes it difficult to do what you are trying to do, which is to substitute a more restrictive schema for the same namespace.

The xs:redefines mechanism was probably invented as an attempted solution to this problem, but it probably creates more problems than it solves, and I don't think it provides a way forward in the Saxon context.

There's an additional problem here, which is that Saxon actually relies on its internal copy of the xpath-functions schema to make sure that xml-to-json operates correctly; if it accepted a user-supplied substitute, then it couldn't rely on the data that's been validated against that schema.

I'm not sure of the overall context in which you are performing this validation, but the easiest answer might be to write your schema for a different namespace, and "project" the document into that namespace for the purpose of validation. You could do that using an XSLT transformation, or with a simple SAX filter, or if performance is critical, an implementation of Saxon's ProxyReceiver.

There might be other approaches that attempt to redirect Saxon to a different version of the schema (for example by using a custom ClassLoader) but they feel precarious.

I'm aware I'm not providing any easy answers here.

RE: SaxonCS: Null reference exception compiling a schema importing another schema that targets the xpath-functions namespace - Added by Jeff Tennessen about 3 years ago

I'll explore using ProxyReceiver, and I very much appreciate that tip. I'm OK with the answers not being easy as long as I can find a solution.

RE: SaxonCS: Null reference exception compiling a schema importing another schema that targets the xpath-functions namespace - Added by Jeff Tennessen about 3 years ago

I've had a chance to look into ProxyReceiver, and I think I understand how to create a derived class that does what I would need it to. I'm not sure how to insert it into the pipeline for validating an instance document, though. If my validation code (including the portion in the original post) looks like this:

Processor proc = new Processor(true);
proc.SetProperty("http://saxon.sf.net/feature/timing", "true");
proc.SetProperty("http://saxon.sf.net/feature/validation-warnings", "false");
List<Error> compileErr = new();
SchemaManager mgr = proc.SchemaManager;
mgr.XsdVersion = "1.1";
mgr.ErrorReporter = (e) => compileErr.Add(e);
mgr.Compile(new Uri("B.xsd"));
List<ValidationFailure> valErrs = new();
SchemaValidator validator = mgr.NewSchemaValidator();
validator.InvalidityListener = (e) => valErrs.Add(e);
validator.Validate(new Uri("B.xml"));

could you nudge me in the right direction concerning how I would include the new subclass of ProxyReceiver? Or do I need to take a different approach to validation altogether in order to include it?

RE: SaxonCS: Null reference exception compiling a schema importing another schema that targets the xpath-functions namespace - Added by Michael Kay about 3 years ago

I'm sorry, I forgot that this was SaxonCS where the API offers rather fewer opportunities to dive down into Saxon internals. On the Java product the SchemaValidator is a Destination, which makes it fairly easy to push events to it from a Receiver pipeline, but it's not obvious whether that's possible in SaxonCS. The underlying "java" SchemaValidator is hidden in an internal property. Probably we should add a method SchemaValidator.asDestination() which enables you to pipe things into the SchemaValidator.

With the API as it currently stands, I think the only way of validating a filtered XML stream is by writing the filter to implement XmlReader, and that's a very messy interface to implement, because of the sheer number of methods that have to delegate to the upstream producer.

RE: SaxonCS: Null reference exception compiling a schema importing another schema that targets the xpath-functions namespace - Added by Jeff Tennessen about 3 years ago

OK, understood. I'll keep working on potential solutions and watching to see if an asDestination() method gets added to SchemaValidator. ;-) I appreciate all your help.

    (1-7/7)

    Please register to reply