123456789_123456789_123456789_123456789_123456789_

Class: Nokogiri::XML::Schema

Relationships & Source Files
Extension / Inclusion / Inheritance Descendants
Subclasses:
Inherits: Object
Defined in: lib/nokogiri/xml/schema.rb,
ext/nokogiri/xml_schema.c

Overview

Schema is used for validating XML against an XSD schema definition.

⚠ Since v1.11.0, Schema treats inputs as untrusted by default, and so external entities are not resolved from the network (http:// or ftp://). When parsing a trusted document, the caller may turn off the NONET option via the ParseOptions to (re-)enable external entity resolution over a network connection.

🛡 Before v1.11.0, documents were “trusted” by default during schema parsing which was counter to Nokogiri’s “untrusted by default” security policy.

Example: Determine whether an XML document is valid.

schema = Nokogiri::XML::Schema.new(File.read(XSD_FILE))
doc = Nokogiri::XML::Document.parse(File.read(XML_FILE))
schema.valid?(doc) # Boolean

Example: Validate an XML document against an XSD schema, and capture any errors that are found.

schema = Nokogiri::XML::Schema.new(File.read(XSD_FILE))
doc = Nokogiri::XML::Document.parse(File.read(XML_FILE))
errors = schema.validate(doc) # Array<SyntaxError>

Example: Validate an XML document using a Document containing an XSD schema definition.

schema_doc = Nokogiri::XML::Document.parse(File.read(RELAX_NG_FILE))
schema = Nokogiri::XML::Schema.from_document(schema_doc)
doc = Nokogiri::XML::Document.parse(File.read(XML_FILE))
schema.valid?(doc) # Boolean

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(input) → Nokogiri::XML::Schema) ⇒ Schema .new(input, parse_options) → Nokogiri::XML::Schema) ⇒ Schema

Parse an XSD schema definition from a String or IO to create a new Schema

Parameters
  • input (String | IO) XSD schema definition

  • #parse_options (Nokogiri::XML::ParseOptions) Defaults to Nokogiri::XML::ParseOptions::DEFAULT_SCHEMA

Returns

Schema

[ GitHub ]

  
# File 'lib/nokogiri/xml/schema.rb', line 69

def self.new(input, parse_options_ = ParseOptions::DEFAULT_SCHEMA, parse_options: parse_options_)
  from_document(Nokogiri::XML::Document.parse(input), parse_options)
end

Class Method Details

.from_document(input) → Nokogiri::XML::Schema) .from_document(input, parse_options) → Nokogiri::XML::Schema)

Parse an XSD schema definition from a Document to create a new Schema

Parameters
  • input (XML::Document) A document containing the XSD schema definition

  • #parse_options (Nokogiri::XML::ParseOptions) Defaults to Nokogiri::XML::ParseOptions::DEFAULT_SCHEMA

Returns

Schema

[ GitHub ]

  
# File 'ext/nokogiri/xml_schema.c', line 169

static VALUE
noko_xml_schema_s_from_document(int argc, VALUE *argv, VALUE rb_class)
{
  /* TODO: deprecate this method and put file-or-string logic into .new so that becomes the
   * preferred entry point, and this can become a private method */
  VALUE rb_document;
  VALUE rb_parse_options;
  VALUE rb_schema;
  xmlDocPtr c_document;
  xmlSchemaParserCtxtPtr c_parser_context;
  int defensive_copy_p = 0;

  rb_scan_args(argc, argv, "11", &rb_document, &rb_parse_options);

  if (!rb_obj_is_kind_of(rb_document, cNokogiriXmlNode)) {
    rb_raise(rb_eTypeError,
             "expected parameter to be a Nokogiri::XML::Document, received %"PRIsVALUE,
             rb_obj_class(rb_document));
  }

  if (!rb_obj_is_kind_of(rb_document, cNokogiriXmlDocument)) {
    xmlNodePtr deprecated_node_type_arg;
    NOKO_WARN_DEPRECATION("Passing a Node as the first parameter to Schema.from_document is deprecated. Please pass a Document instead. This will become an error in Nokogiri v1.17.0."); // TODO: deprecated in v1.15.3, remove in v1.17.0
    Noko_Node_Get_Struct(rb_document, xmlNode, deprecated_node_type_arg);
    c_document = deprecated_node_type_arg->doc;
  } else {
    c_document = noko_xml_document_unwrap(rb_document);
  }

  if (noko_xml_document_has_wrapped_blank_nodes_p(c_document)) {
    // see https://github.com/sparklemotion/nokogiri/pull/2001
    c_document = xmlCopyDoc(c_document, 1);
    defensive_copy_p = 1;
  }

  c_parser_context = xmlSchemaNewDocParserCtxt(c_document);
  rb_schema = xml_schema_parse_schema(rb_class, c_parser_context, rb_parse_options);

  if (defensive_copy_p) {
    xmlFreeDoc(c_document);
    c_document = NULL;
  }

  return rb_schema;
}

.read_memory(input) → Nokogiri::XML::Schema) .read_memory(input, parse_options) → Nokogiri::XML::Schema)

Convenience method for .new

[ GitHub ]

  
# File 'lib/nokogiri/xml/schema.rb', line 78

def self.read_memory(...)
  # TODO deprecate this method
  new(...)
end

Instance Attribute Details

#errors (rw)

The errors found while parsing the XSD

Returns

Array<Nokogiri::XML::SyntaxError>

[ GitHub ]

  
# File 'lib/nokogiri/xml/schema.rb', line 49

attr_accessor :errors

#parse_options (rw)

The options used to parse the schema

Returns

ParseOptions

[ GitHub ]

  
# File 'lib/nokogiri/xml/schema.rb', line 54

attr_accessor :parse_options

Instance Method Details

#valid?(input) → Boolean) ⇒ Boolean

Validate input and return a Boolean indicating whether the document is valid

Parameters
  • input (Nokogiri::XML::Document | String) A parsed document, or a string containing a local filename.

Returns

Boolean

Example: Validate an existing Document

schema = Nokogiri::XML::Schema.new(File.read(XSD_FILE))
return unless schema.valid?(document)

Example: Validate an XML document on disk

schema = Nokogiri::XML::Schema.new(File.read(XSD_FILE))
return unless schema.valid?("/path/to/file.xml")
[ GitHub ]

  
# File 'lib/nokogiri/xml/schema.rb', line 135

def valid?(input)
  validate(input).empty?
end

#validate(input) → Array<SyntaxError>)

Validate input and return any errors that are found.

Parameters
  • input (Nokogiri::XML::Document | String) A parsed document, or a string containing a local filename.

Returns

Array<SyntaxError>

Example: Validate an existing Document, and capture any errors that are found.

schema = Nokogiri::XML::Schema.new(File.read(XSD_FILE))
errors = schema.validate(document)

Example: Validate an XML document on disk, and capture any errors that are found.

schema = Nokogiri::XML::Schema.new(File.read(XSD_FILE))
errors = schema.validate("/path/to/file.xml")
[ GitHub ]

  
# File 'lib/nokogiri/xml/schema.rb', line 104

def validate(input)
  if input.is_a?(Nokogiri::XML::Document)
    validate_document(input)
  elsif File.file?(input)
    validate_file(input)
  else
    raise ArgumentError, "Must provide Nokogiri::XML::Document or the name of an existing file"
  end
end

#validate_document(document) (private)

[ GitHub ]

  
# File 'ext/nokogiri/xml_schema.c', line 20

static VALUE
noko_xml_schema__validate_document(VALUE self, VALUE document)
{
  xmlDocPtr doc;
  xmlSchemaPtr schema;
  xmlSchemaValidCtxtPtr valid_ctxt;
  VALUE errors;

  TypedData_Get_Struct(self, xmlSchema, &xml_schema_type, schema);
  doc = noko_xml_document_unwrap(document);

  errors = rb_ary_new();

  valid_ctxt = xmlSchemaNewValidCtxt(schema);

  if (NULL == valid_ctxt) {
    /* we have a problem */
    rb_raise(rb_eRuntimeError, "Could not create a validation context");
  }

  xmlSchemaSetValidStructuredErrors(
    valid_ctxt,
    noko__error_array_pusher,
    (void *)errors
  );

  int status = xmlSchemaValidateDoc(valid_ctxt, doc);

  xmlSchemaFreeValidCtxt(valid_ctxt);

  if (status != 0) {
    if (RARRAY_LEN(errors) == 0) {
      rb_ary_push(errors, rb_str_new2("Could not validate document"));
    }
  }

  return errors;
}

#validate_file(rb_filename) (private)

[ GitHub ]

  
# File 'ext/nokogiri/xml_schema.c', line 59

static VALUE
noko_xml_schema__validate_file(VALUE self, VALUE rb_filename)
{
  xmlSchemaPtr schema;
  xmlSchemaValidCtxtPtr valid_ctxt;
  const char *filename ;
  VALUE errors;

  TypedData_Get_Struct(self, xmlSchema, &xml_schema_type, schema);
  filename = (const char *)StringValueCStr(rb_filename) ;

  errors = rb_ary_new();

  valid_ctxt = xmlSchemaNewValidCtxt(schema);

  if (NULL == valid_ctxt) {
    /* we have a problem */
    rb_raise(rb_eRuntimeError, "Could not create a validation context");
  }

  xmlSchemaSetValidStructuredErrors(
    valid_ctxt,
    noko__error_array_pusher,
    (void *)errors
  );

  int status = xmlSchemaValidateFile(valid_ctxt, filename, 0);

  xmlSchemaFreeValidCtxt(valid_ctxt);

  if (status != 0) {
    if (RARRAY_LEN(errors) == 0) {
      rb_ary_push(errors, rb_str_new2("Could not validate file."));
    }
  }

  return errors;
}