XML validation

From Rosetta Code
XML validation is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Given an XML document and an XSD schema definition validate that the document follows the schema described.

Sample XML and XSD for tests can be found on this W3 Schools page.

C

Library: LibXML

At the time of writing, the XML and XSD files at the URLs used in the other examples were inaccessible. The files from the W3 Schools page were used for tests. <lang C>

  1. include <libxml/xmlschemastypes.h>

int main(int argC, char** argV) { if (argC <= 2) { printf("Usage: %s <XML Document Name> <XSD Document Name>\n", argV[0]); return 0; }

xmlDocPtr doc; xmlSchemaPtr schema = NULL; xmlSchemaParserCtxtPtr ctxt; char *XMLFileName = argV[1]; char *XSDFileName = argV[2]; int ret;

xmlLineNumbersDefault(1);

ctxt = xmlSchemaNewParserCtxt(XSDFileName);

xmlSchemaSetParserErrors(ctxt, (xmlSchemaValidityErrorFunc) fprintf, (xmlSchemaValidityWarningFunc) fprintf, stderr); schema = xmlSchemaParse(ctxt); xmlSchemaFreeParserCtxt(ctxt);


doc = xmlReadFile(XMLFileName, NULL, 0);

if (doc == NULL){ fprintf(stderr, "Could not parse %s\n", XMLFileName); } else{ xmlSchemaValidCtxtPtr ctxt;

ctxt = xmlSchemaNewValidCtxt(schema); xmlSchemaSetValidErrors(ctxt, (xmlSchemaValidityErrorFunc) fprintf, (xmlSchemaValidityWarningFunc) fprintf, stderr); ret = xmlSchemaValidateDoc(ctxt, doc);

if (ret == 0){ printf("%s validates\n", XMLFileName); } else if (ret > 0){ printf("%s fails to validate\n", XMLFileName); } else{ printf("%s validation generated an internal error\n", XMLFileName); } xmlSchemaFreeValidCtxt(ctxt); xmlFreeDoc(doc); }


if(schema != NULL) xmlSchemaFree(schema);

xmlSchemaCleanupTypes(); xmlCleanupParser(); xmlMemoryDump();

return 0; } </lang> Output, files used from the W3 Schools page :

C:\rosettaCode>xmlValidator.exe shiporder.xml shiporder.xsd
shiporder.xml validates

C#

<lang csharp> using System; using System.Xml; using System.Xml.Schema; using System.IO;

public class Test { public static void Main() { // your code goes here XmlSchemaSet sc = new XmlSchemaSet(); sc.Add(null, "http://venus.eas.asu.edu/WSRepository/xml/Courses.xsd"); XmlReaderSettings settings = new XmlReaderSettings(); settings.ValidationType = ValidationType.Schema; settings.Schemas = sc; settings.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack); // Create the XmlReader object. XmlReader reader = XmlReader.Create("http://venus.eas.asu.edu/WSRepository/xml/Courses.xml", settings); // Parse the file. while (reader.Read()); // will call event handler if invalid Console.WriteLine("The XML file is valid for the given xsd file"); }

// Display any validation errors. private static void ValidationCallBack(object sender, ValidationEventArgs e) { Console.WriteLine("Validation Error: {0}", e.Message); } } </lang>

F#

Using an inline stylesheet:

<lang fsharp>open System.Xml open System.Xml.Schema open System.IO

let xml = @"<root> <xs:schema id='an-element' targetNamespace='example' xmlns:mstns='example' xmlns='example' xmlns:xs='http://www.w3.org/2001/XMLSchema' attributeFormDefault='unqualified' elementFormDefault='qualified'>

 <xs:element name='an-element'>
   <xs:complexType>
     <xs:sequence minOccurs='0' maxOccurs='unbounded'>
       <xs:element name='another-element' nillable='true'>
         <xs:complexType>
           <xs:simpleContent>
             <xs:extension base='xs:string'>
               <xs:attribute name='an-attribute' form='unqualified' type='xs:boolean' />
             </xs:extension>
           </xs:simpleContent>
         </xs:complexType>
       </xs:element>
     </xs:sequence>
   </xs:complexType>
 </xs:element>

</xs:schema> <an-element xmlns='example'>

   <another-element an-attribute='false'>...</another-element>
   <another-element an-attribute='wrong'>123</another-element>

</an-element> </root>"

let validationData withWarnings =

   let errors = ref 0
   let warnings = ref 0
   fun input ->
       match input with
       | Some(msg, severity) ->
           if severity = XmlSeverityType.Error then
               errors := !errors + 1
               printfn "Validation error: %s" msg
           elif withWarnings then
               warnings := !warnings + 1
               printfn "Validation warning: %s" msg
           None
       | None ->
           if withWarnings then
               Some(dict[XmlSeverityType.Error, !errors; XmlSeverityType.Warning, !warnings])
           else
               Some(dict[XmlSeverityType.Error, !errors])

[<EntryPoint>] let main argv =

   let withWarnings = argv.Length > 0 && argv.[0] = "-w"
   let vData = validationData withWarnings
   let validationEvent = new ValidationEventHandler(fun _ e ->
       vData (Some(e.Message, e.Severity)) |> ignore)
   let settings = new XmlReaderSettings()
   settings.ValidationType <- ValidationType.Schema
   settings.ValidationEventHandler.AddHandler(validationEvent)
   settings.ValidationFlags <- settings.ValidationFlags ||| XmlSchemaValidationFlags.ProcessInlineSchema ||| XmlSchemaValidationFlags.ReportValidationWarnings
   let reader = XmlReader.Create(new StringReader(xml), settings);
   while reader.Read() do ()
   printfn "%A" (Seq.toList (vData None).Value)
   0

</lang>

Output:
>RosettaCode
Validation error: The 'an-attribute' element is invalid - The value 'wrong' is invalid according to its datatype 'http://www.w3.org/2001/XMLSchema:boolean' - The string 'wrong' is not a valid boolean value.
[[Error, 1]]

>RosettaCode -w
Validation warning: Could not find schema information for the element 'root'.
Validation error: The 'an-attribute' element is invalid - The value 'wrong' is invalid according to its datatype 'http://www.w3.org/2001/XMLSchema:boolean' - The string 'wrong' is not a valid boolean value.
[[Error, 1]; [Warning, 1]]

Changing wrong to a boolean, e. g. true, The result (without -w) is

[[Error, 0]]

Go

Library: libxml2(Go)


This uses the w3schools test data linked to above. <lang go>package main

import (

   "fmt"
   "github.com/lestrrat-go/libxml2"
   "github.com/lestrrat-go/libxml2/xsd"
   "io/ioutil"
   "log"
   "os"

)

func check(err error) {

   if err != nil {
       log.Fatal(err)
   }

}

func main() {

   xsdfile := "shiporder.xsd"
   f, err := os.Open(xsdfile)
   check(err)
   defer f.Close()
   buf, err := ioutil.ReadAll(f)
   check(err)
   s, err := xsd.Parse(buf)
   check(err)
   defer s.Free()
   xmlfile := "shiporder.xml"
   f2, err := os.Open(xmlfile)
   check(err)
   defer f2.Close()
   buf2, err := ioutil.ReadAll(f2)
   check(err)
   d, err := libxml2.Parse(buf2)
   check(err)
   if err := s.Validate(d); err != nil {
       for _, e := range err.(xsd.SchemaValidationError).Errors() {
           log.Printf("error: %s", e.Error())
       }
       return
   }
   fmt.Println("Validation of", xmlfile, "against", xsdfile, "successful!")

}</lang>

Output:
Validation of shiporder.xml against shiporder.xsd successful!

Groovy

Translation of: Java

Solution: <lang groovy>import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI import javax.xml.transform.stream.StreamSource import javax.xml.validation.SchemaFactory import org.xml.sax.SAXParseException

def factory = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI) def validate = { schemaURL, docURL ->

   try {
       factory.newSchema(schemaURL.toURL()).newValidator().validate(new StreamSource(docURL))
       true
   } catch (SAXParseException e) {
       false
   }

}</lang>

Test: <lang groovy>def schemaLoc = "http://venus.eas.asu.edu/WSRepository/xml/Courses.xsd" def docLoc = "http://venus.eas.asu.edu/WSRepository/xml/Courses.xml" println "Document is ${validate(schemaLoc, docLoc)? 'valid' : 'invalid'}"</lang>

Java

<lang java>import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI;

import java.net.MalformedURLException; import java.net.URL;

import javax.xml.transform.stream.StreamSource; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; import javax.xml.ws.Holder;

import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException;

public class XmlValidation { public static void main(String... args) throws MalformedURLException { URL schemaLocation = new URL("http://venus.eas.asu.edu/WSRepository/xml/Courses.xsd"); URL documentLocation = new URL("http://venus.eas.asu.edu/WSRepository/xml/Courses.xml"); if (validate(schemaLocation, documentLocation)) { System.out.println("document is valid"); } else { System.out.println("document is invalid"); } }

// The least code you need for validation public static boolean minimalValidate(URL schemaLocation, URL documentLocation) { SchemaFactory factory = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI); try { Validator validator = factory.newSchema(schemaLocation).newValidator(); validator.validate(new StreamSource(documentLocation.toString())); return true; } catch (Exception e) { return false; } }

// A more complete validator public static boolean validate(URL schemaLocation, URL documentLocation) { SchemaFactory factory = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI); final Holder<Boolean> valid = new Holder<>(true); try { Validator validator = factory.newSchema(schemaLocation).newValidator(); // Get some better diagnostics out validator.setErrorHandler(new ErrorHandler(){ @Override public void warning(SAXParseException exception) { System.out.println("warning: " + exception.getMessage()); }

@Override public void error(SAXParseException exception) { System.out.println("error: " + exception.getMessage()); valid.value = false; }

@Override public void fatalError(SAXParseException exception) throws SAXException { System.out.println("fatal error: " + exception.getMessage()); throw exception; }}); validator.validate(new StreamSource(documentLocation.toString())); return valid.value; } catch (SAXException e) { // Already reported above return false; } catch (Exception e) { // If this is the only thing that throws, it's a gross error System.err.println(e); return false; } } }</lang>

Julia

<lang julia>using LightXML

const Xptr = LightXML.Xptr

function validate(url::String, schemafile::String)

   ctxt = ccall((:xmlSchemaNewParserCtxt, LightXML.libxml2), Xptr, (Cstring,), schemafile)
   ctxt != C_NULL || throw(LightXML.XMLNoRootError())
   schema = ccall((:xmlSchemaParse, LightXML.libxml2), Xptr, (Xptr,), ctxt)
   schema != C_NULL || throw(LightXML.XMLNoRootError())
   ccall((:xmlSchemaFreeParserCtxt, LightXML.libxml2), Cvoid, (Xptr,), ctxt)
   ctxt = ccall((:xmlSchemaNewValidCtxt, LightXML.libxml2), Xptr, (Xptr,), schema)
   err = ccall((:xmlSchemaValidateFile, LightXML.libxml2), 
       Cint, (Ptr{LightXML.xmlBuffer}, Cstring), ctxt, url)
   return err == 0 ? true : false

end

function xsdvalidatexml()

   if length(ARGS) != 2
       println("		Usage: julia ", PROGRAM_FILE, ", xmlfilename xsdfilename")
   elseif validate(ARGS[1], ARGS[2])
       println("File ", ARGS[1], " validates as correct XML using the XSD file ", ARGS[2], ".")
   else
       println("File ", ARGS[1]. "does not validate.")
   end

end

xsdvalidatexml() </lang>

Kotlin

Translation of: C
Library: libxml2
Works with: Ubuntu 14.04

Assuming that libxml2 is already installed on your system in the default location(s), you first need to build libxml_schemas.klib using the following .def file and the cinterop tool:

// libxml_schemas.def
headers = /usr/include/libxml2/libxml/xmlschemastypes.h
compilerOpts = -I/usr/include/libxml2
linkerOpts = -L/usr/lib/x86_64-linux-gnu -lxml2

Next, you need to compile the following Kotlin program, linking against libxml_schemas.klib. <lang scala>// Kotlin Native v0.6

import kotlinx.cinterop.* import platform.posix.* import libxml_schemas.*

fun err(ctx: COpaquePointer?, msg: CPointer<ByteVar>?, extra: CPointer<ByteVar>?) {

   val fp = ctx?.reinterpret<FILE>()
   fprintf(fp, msg?.toKString(), extra?.toKString())

}

fun warn(ctx: COpaquePointer?, msg: CPointer<ByteVar>?, extra: CPointer<ByteVar>?) {

   err(ctx, msg, extra)

}

fun main(args: Array<String>) {

   if (args.size != 2) {
       println("You need to pass exactly 2 command line arguments, namely:")
       println("    <XML Document Name> <XSD Document Name>")
       return
   }
   val xmlFileName = args[0]
   val xsdFileName = args[1]
   xmlLineNumbersDefault(1)
   val ctxt = xmlSchemaNewParserCtxt(xsdFileName)
   xmlSchemaSetParserErrors(
       ctxt,
       staticCFunction(::err) as xmlSchemaValidityErrorFunc?,
       staticCFunction(::warn) as xmlSchemaValidityWarningFunc?,
       stderr
   )
   val schema = xmlSchemaParse(ctxt)
   xmlSchemaFreeParserCtxt(ctxt)
   val doc = xmlReadFile(xmlFileName, null, 0)
   if (doc == null) {
       println("Could not parse $xmlFileName")
   }
   else {
       val ctxt2 = xmlSchemaNewValidCtxt(schema)
       xmlSchemaSetValidErrors(
           ctxt2,
           staticCFunction(::err) as xmlSchemaValidityErrorFunc?,
           staticCFunction(::warn) as xmlSchemaValidityWarningFunc?,
           stderr
       )
       val ret = xmlSchemaValidateDoc(ctxt2, doc)
       if (ret == 0)
           println("$xmlFileName validates")
       else if (ret > 0)
           println("$xmlFileName fails to validate")
       else
           println("$xmlFileName generated an internal error")
       xmlSchemaFreeValidCtxt(ctxt2)
       xmlFreeDoc(doc)
   }
   if (schema != null) xmlSchemaFree(schema)
   xmlSchemaCleanupTypes()
   xmlCleanupParser()
   xmlMemoryDump()

}</lang> Finally, the resulting .kexe file should be executed passing it similar command line arguments to the C entry to produce the following output.

$ ./xmlval.kexe shiporder.xml shiporder.xsd
shiporder.xml validates

Nim

Library: libxml2

As in the C version, we use “libxml2” for the validation. As there is no Nim binding for this library, we provide the necessary definitions in our program (three types and six procedures). There is nothing more to do. In Nim, interfacing with C is often very easy.

Our program is based on the C version with many differences. In particular, we use the function “xmlSchemaValidateFile” rather than the function “xmlSchemaValidateDoc”.

<lang Nim>import os, strformat

const LibXml = "libxml2.so"

type

 XmlSchemaParserCtxtPtr = pointer
 XmlSchemaPtr = pointer
 XmlSchemaValidCtxtPtr = pointer


  1. Declaration of needed "libxml2" procedures.

proc xmlSchemaNewParserCtxt(url: cstring): XmlSchemaParserCtxtPtr

 {.cdecl, dynlib: LibXml, importc: "xmlSchemaNewParserCtxt".}

proc xmlSchemaParse(ctxt: XmlSchemaParserCtxtPtr): XmlSchemaPtr

 {.cdecl, dynlib: LibXml, importc: "xmlSchemaParse".}

proc xmlSchemaFreeParserCtxt(ctxt: XmlSchemaParserCtxtPtr)

 {.cdecl, dynlib: LibXml, importc: "xmlSchemaFreeParserCtxt".}

proc xmlSchemaNewValidCtxt(schema: XmlSchemaPtr): XmlSchemaValidCtxtPtr

 {.cdecl, dynlib: LibXml, importc: "xmlSchemaNewValidCtxt".}

proc xmlSchemaValidateFile(ctxt: XmlSchemaValidCtxtPtr; filename: cstring; options: cint): cint

 {.cdecl, dynlib: LibXml, importc: "xmlSchemaValidateFile".}

proc xmlSchemaFreeValidCtxt(ctxt: XmlSchemaValidCtxtPtr)

 {.cdecl, dynlib: LibXml, importc: "xmlSchemaFreeValidCtxt".}


if paramCount() != 2:

 quit &"Usage: {getAppFilename().lastPathPart} <XML Document Name> <XSD Document Name", QuitFailure

let xmlFilename = paramStr(1) let xsdFilename = paramStr(2)

  1. Parse XML schema file.

let parserCtxt = xmlSchemaNewParserCtxt(xsdFilename) let schema = parserCtxt.xmlSchemaParse() parserCtxt.xmlSchemaFreeParserCtxt()

  1. Validate XML file using XML schema.

let validCtxt = schema.xmlSchemaNewValidCtxt() case validCtxt.xmlSchemaValidateFile(xmlFilename, 0) of 0:

 echo &"“{xmlFilename}” validates."

of -1:

 echo &"“{xmlFilename}” validation generated an internal error."

else:

 echo &"“{xmlFilename}” fails to validate."

validCtxt.xmlSchemaFreeValidCtxt()</lang>

Output:

Using command ./xml_validate shiporder.xml shiporder.xsd:

“shiporder.xml” validates.

Using a modified file “shiporder1.xml” where tag “orderperson” has been replaced by “orderperson1”:

Entity: line 6: Schemas validity error : Element 'orderperson1': This element is not expected. Expected is ( orderperson ).
“shiporder1.xml” fails to validate.

Perl

<lang perl>#!/usr/bin/env perl -T use 5.018_002; use warnings; use Try::Tiny; use XML::LibXML;

our $VERSION = 1.000_000;

my $parser = XML::LibXML->new();

my $good_xml = '<a>5</a>'; my $bad_xml = '<a>5foobar</a>'; my $xmlschema_markup = <<'END'; <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

 <xsd:element name="a" type="xsd:integer"/>

</xsd:schema> END

my $xmlschema = XML::LibXML::Schema->new( string => $xmlschema_markup );

for ( $good_xml, $bad_xml ) {

   my $doc = $parser->parse_string($_);
   try {
       $xmlschema->validate($doc);
   }
   finally {
       if (@_) {
           say "Not valid: @_";
       }
       else {
           say 'Valid';
       }
   };

}</lang>

Output:
Valid
Not valid: unknown-7fe99976a9a0:0: Schemas validity error :
  Element 'a': Element content is not allowed, because the type definition is simple.

Phix

Translation of: C

Note that error handling has been delegated to within libxml.e, specifically xmlSchemaNewParserCtxt() and xmlSchemaNewValidCtxt() assign an internal handler, which resorts to xmlGetLastError() and therefore needs to use a bit of cffi, that is rather than using the (odd-looking) error it is actually passed. The libxml.e wrapper was penned specifically for this task and is just about as bare-bones as it could ever possibly be, and win32-only, for now.

--
-- demo\rosetta\Validate_XML.exw
-- =============================
--
without js
requires(32) -- let me know if you can get this to work on 64bit
requires(WINDOWS) --   ""   if you can get this to work on linux
constant dl = `Download rosetta\libxml\ from http://phix.x10.mx/pmwiki/pmwiki.php?n=Main.libxml`
assert(get_file_type("libxml")=FILETYPE_DIRECTORY,dl)
include libxml\libxml.e

constant XSDFileName = `libxml\shiporder.xsd`, -- (versions 2 & 3 also included in "")
         XMLFileName = `libxml\shiporder.xml`

xmlLineNumbersDefault(1)
atom pctxt = xmlSchemaNewParserCtxt(XSDFileName),
     schema = xmlSchemaParse(pctxt)
xmlSchemaFreeParserCtxt(pctxt)
atom doc = xmlReadFile(XMLFileName, NULL, 0)
if doc=NULL then
    printf(2, "Could not parse %s\n", {XMLFileName})
else
    atom vctxt = xmlSchemaNewValidCtxt(schema)
    integer ret = xmlSchemaValidateDoc(vctxt, doc)
    if ret=0 then
        printf(1,"%s validates\n", {XMLFileName})
    elsif ret>0 then
        printf(1,"%s fails to validate\n", {XMLFileName})
    else
        printf(1,"%s validation generated an internal error\n", {XMLFileName})
    end if
    xmlSchemaFreeValidCtxt(vctxt)
    xmlFreeDoc(doc)
end if
if schema!=NULL then
    xmlSchemaFree(schema)
end if
xmlSchemaCleanupTypes()
xmlCleanupParser()
xmlMemoryDump()
Output:
libxml\shiporder.xml validates

Performing the same error handling check as Nim, ie changing orderperson to orderperson1 (twice) results in:

Error/Warning in line 6
Element 'orderperson1': This element is not expected. Expected is ( orderperson ).
libxml\shiporder.xml fails to validate

All three formats of the xsd file as given on the W3 page give identical results, for both with and without that error.

PHP

<lang php> libxml_use_internal_errors(true);

$xml = new DOMDocument(); $xml->load('shiporder.xml');

if (!$xml->schemaValidate('shiporder.xsd')) {

   var_dump(libxml_get_errors()); exit;

} else {

   echo 'success';

} </lang>

Output:
using valid shiporder.xml and shiporder.xsd:
success
Done.
using invalid shiporder.xml and valid shiporder.xsd:
array(1) {
  [0] =>
  class LibXMLError#2 (6) {
    public $level =>
    int(2)
    public $code =>
    int(1871)
    public $column =>
    int(0)
    public $message =>
    string(74) "Element 'foo': This element is not expected. Expected is ( orderperson ).
"
    public $file =>
    string(43) "file:/C:/xampp/htdocs/rosetta/shiporder.xml"
    public $line =>
    int(4)
  }
}
Done.

Python

<lang python>#!/bin/python from __future__ import print_function import lxml from lxml import etree

if __name__=="__main__":

parser = etree.XMLParser(dtd_validation=True) schema_root = etree.XML(\ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:element name="a" type="xsd:integer"/> </xsd:schema> ) schema = etree.XMLSchema(schema_root)

#Good xml parser = etree.XMLParser(schema = schema) try: root = etree.fromstring("<a>5</a>", parser) print ("Finished validating good xml") except lxml.etree.XMLSyntaxError as err: print (err)

#Bad xml parser = etree.XMLParser(schema = schema) try: root = etree.fromstring("<a>5foobar</a>", parser) except lxml.etree.XMLSyntaxError as err: print (err)</lang>

Output:
Finished validating good xml
Element 'a': Element content is not allowed, because the type definition is simple.

Raku

(formerly Perl 6)

Translation of: Perl

<lang perl6>

  1. Reference: https://github.com/libxml-raku/LibXML-raku

use v6.d; use LibXML; use LibXML::Schema;

my $good_xml = '<a>5</a>'; my $bad_xml = '<a>5foobar</a>';

my $xsdschema = q:to<EOF>;

  <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
     <xsd:element name="a" type="xsd:integer"/>
  </xsd:schema>

EOF

my LibXML $p .= new();

for ( $good_xml, $bad_xml ) {

  my $x = $p.parse: :string($_);
  try { LibXML::Schema.new( string => $xsdschema ).validate( $x ) }
  !$! ?? say "Valid." !! say $!.message() ;

}</lang>

Output:
Valid.
Schemas validity error : Element 'a': Element content is not allowed, because the type definition is simple.

Scala

 <lang Scala>import java.net.URL

import javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI import javax.xml.transform.stream.StreamSource import javax.xml.validation.SchemaFactory import javax.xml.ws.Holder import org.xml.sax.{ErrorHandler, SAXException, SAXParseException}

object XmlValidation extends App {

 val (schemaLocation, documentLocation) = (new URL("http://venus.eas.asu.edu/WSRepository/xml/Courses.xsd")
   , new URL("http://venus.eas.asu.edu/WSRepository/xml/Courses.xml"))
 println(s"Document is ${if (validate(schemaLocation, documentLocation)) "valid" else "invalid"}.")
 // A more complete validator
 def validate(schemaLocation: URL, documentLocation: URL): Boolean = {
   val factory = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI)
   val valid = new Holder[Boolean](true)
   try {
     val validator = factory.newSchema(schemaLocation).newValidator
     // Get some better diagnostics out
     validator.setErrorHandler(new ErrorHandler() {
       override def warning(exception: SAXParseException) = println("warning: " + exception.getMessage)
       override def error(exception: SAXParseException) = {
         println("error: " + exception.getMessage)
         valid.value = false
       }
       override def fatalError(exception: SAXParseException) = {
         println("fatal error: " + exception.getMessage)
         throw exception
       }
     })
     validator.validate(new StreamSource(documentLocation.toString))
     valid.value
   } catch {
     case _: SAXException =>
       // Already reported above
       false
     case e: Exception =>
       // If this is the only thing that throws, it's a gross error
       println(e)
       false
   }
 }

}</lang>

Sidef

Translation of: Perl

<lang ruby>require('XML::LibXML')

func is_valid_xml(str, schema) {

   var parser    = %O<XML::LibXML>.new
   var xmlschema = %O<XML::LibXML::Schema>.new(string => schema)
   try {
       xmlschema.validate(parser.parse_string(str))
       true
   } catch {
       false
   }

}

var good_xml = '<a>5</a>' var bad_xml = '<a>5foobar</a>'

var xmlschema_markup = <<'END' <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

 <xsd:element name="a" type="xsd:integer"/>

</xsd:schema> END

[good_xml, bad_xml].each { |xml|

   say "is_valid_xml(#{xml.dump}) : #{is_valid_xml(xml, xmlschema_markup)}"

}</lang>

Output:
is_valid_xml("<a>5</a>") : true
is_valid_xml("<a>5<b>foobar</b></a>") : false

Visual Basic .NET

Compiler: Roslyn Visual Basic (language version >= 14, e.g. with Visual Studio 2015)

Works with: .NET Core version 2.1

<lang vbnet>Option Compare Binary Option Explicit On Option Infer On Option Strict On

Imports System.Xml Imports System.Xml.Schema

Module Program

   Function GetValidationErrors(doc As XDocument, schemaSet As XmlSchemaSet) As IList(Of ValidationEventArgs)
       GetValidationErrors = New List(Of ValidationEventArgs)
       doc.Validate(schemaSet, Sub(sender, e) GetValidationErrors.Add(e))
   End Function
   Sub Main()
       ' These functions are declared in another module found below.
       Dim schema = GetSchema()
       Dim document = GetDocument()
       Dim schemaSet As New XmlSchemaSet()
       schemaSet.Add(XmlSchema.Read(schema.CreateReader(), Nothing))
       Dim errors = GetValidationErrors(document, schemaSet)
       For Each e In errors
           Console.WriteLine($"Validation {e.Severity}:{vbCrLf}{e.Message}")
       Next
       If errors.Count = 0 Then Console.WriteLine("The document is valid.")
   End Sub

End Module</lang>

Output:
Validation Error:
The 'an-attribute' attribute is invalid - The value 'wrong' is invalid according to its datatype 'http://www.w3.org/2001/XMLSchema:boolean' - The string 'wrong' is not a valid Boolean value.

An alternative is to use XmlReader (like the C# and F# examples [as of 2019-08-06]).

<lang vbnet> Function GetValidationErrorsXmlReader(doc As XDocument, schemaSet As XmlSchemaSet, warnings As Boolean) As IList(Of ValidationEventArgs)

       GetValidationErrorsReader = New List(Of ValidationEventArgs)
       Dim settings As New XmlReaderSettings()
       With settings
           .ValidationType = ValidationType.Schema
           .Schemas = schemaSet
           If warnings Then .ValidationFlags = .ValidationFlags Or XmlSchemaValidationFlags.ReportValidationWarnings
       End With
       AddHandler settings.ValidationEventHandler, Sub(sender, e) GetValidationErrorsReader.Add(e)
       Using reader = XmlReader.Create(doc.CreateReader(), settings)
           Do While reader.Read() : Loop
       End Using
   End Function</lang>

Creating the documents (same as F#) from strings (does not handle syntax errors):

<lang vbnet>Module Constants

   Const SCHEMA As String =

"<?xml version='1.0'?> <xs:schema id='an-element' targetNamespace='example' xmlns:mstns='example' xmlns='example' xmlns:xs='http://www.w3.org/2001/XMLSchema' attributeFormDefault='unqualified' elementFormDefault='qualified'>

   <xs:element name='an-element'>
       <xs:complexType>
           <xs:sequence minOccurs='0' maxOccurs='unbounded'>
               <xs:element name='another-element' nillable='true'>
                   <xs:complexType>
                       <xs:simpleContent>
                           <xs:extension base='xs:string'>
                               <xs:attribute name='an-attribute' form='unqualified' type='xs:boolean'/>
                           </xs:extension>
                       </xs:simpleContent>
                   </xs:complexType>
               </xs:element>
           </xs:sequence>
       </xs:complexType>
   </xs:element>

</xs:schema>"

   Const DOCUMENT As String =

"<?xml version='1.0'?> <an-element xmlns='example'>

   <another-element an-attribute='false'>...</another-element>
   <another-element an-attribute='wrong'>123</another-element>

</an-element>"

   Function GetSchema() As XDocument
       Return XDocument.Parse(SCHEMA)
   End Function
   Function GetDocument() As XDocument
       Return XDocument.Parse(DOCUMENT)
   End Function

End Module</lang>

Alternatively, we can be cheeky and use VB's XML literals...

<lang vbnet>Module Constants

   Function GetDocument() As XDocument
       Return _
       <?xml version="1.0"?>
       <an-element xmlns="example">
           <another-element an-attribute="false">...</another-element>
           <another-element an-attribute="wrong"> 123</another-element>
       </an-element>
   End Function
   Function GetSchema() As XDocument
       Return _
       <?xml version="1.0"?>
       <xs:schema id="an-element" targetNamespace="example" xmlns:mstns="example" xmlns="example" xmlns:xs="http://www.w3.org/2001/XMLSchema" attributeFormDefault="unqualified" elementFormDefault="qualified">
           <xs:element name="an-element">
               <xs:complexType>
                   <xs:sequence minOccurs="0" maxOccurs="unbounded">
                       <xs:element name="another-element" nillable="true">
                           <xs:complexType>
                               <xs:simpleContent>
                                   <xs:extension base="xs:string">
                                       <xs:attribute name="an-attribute" form="unqualified" type="xs:boolean"/>
                                   </xs:extension>
                               </xs:simpleContent>
                           </xs:complexType>
                       </xs:element>
                   </xs:sequence>
               </xs:complexType>
           </xs:element>
       </xs:schema>
   End Function

End Module</lang>

Wren

Translation of: Nim
Library: libxml2

Wren has no XML support whatever, either built-in or (AFAIK) via third parties. We therefore use an embedded program so we can ask the C host to communicate with Libxml2 for us. <lang ecmascript>/* xml_validation.wren */

class Args {

   foreign static xmlFilename
   foreign static xsdFilename

}

foreign class XmlSchemaPtr {

   construct new() {}

}

foreign class XmlSchemaParserCtxtPtr {

   construct new(url) {}
   foreign parse(schema)
   foreign freeParserCtxt()

}

foreign class XmlSchemaValidCtxtPtr {

   construct new(schema) {}
   foreign validateFile(filename, options)
   foreign freeValidCtxt()

}

var xmlFilename = Args.xmlFilename var xsdFilename = Args.xsdFilename

// parse xml schema file var parserCtxt = XmlSchemaParserCtxtPtr.new(xsdFilename) var schema = XmlSchemaPtr.new() parserCtxt.parse(schema) parserCtxt.freeParserCtxt()

// validate xml file using schema var validCtxt = XmlSchemaValidCtxtPtr.new(schema) var res = validCtxt.validateFile(xmlFilename, 0) if (res == 0) {

   System.print("'%(xmlFilename)' validates.")

} else if (res == -1) {

   System.print("'%(xmlFilename)' validation generated an internal error.")

} else {

   System.print("'%(xmlFilename)' fails to validate.")

} validCtxt.freeValidCtxt()</lang>
We now embed this in the following C program, compile and run it. <lang c>/* built with: gcc xml_validation.c -o xml_validation -I/usr/include/libxml2 -lxml2 -lwren -lm */

  1. include <stdio.h>
  2. include <stdlib.h>
  3. include <string.h>
  4. include <libxml2/libxml/xmlschemastypes.h>
  5. include "wren.h"

/* C <=> Wren interface functions */

char *xmlFilename, *xsdFilename;

void C_xmlFilename(WrenVM* vm) {

   wrenSetSlotString(vm, 0, xmlFilename);

}

void C_xsdFilename(WrenVM* vm) {

   wrenSetSlotString(vm, 0, xsdFilename);

}

void C_xmlSchemaPtrAllocate(WrenVM* vm) {

   wrenSetSlotNewForeign(vm, 0, 0, sizeof(xmlSchemaPtr));

}

void C_xmlSchemaParserCtxtPtrAllocate(WrenVM* vm) {

   xmlSchemaParserCtxtPtr* pctxt = (xmlSchemaParserCtxtPtr*)wrenSetSlotNewForeign(vm, 0, 0, sizeof(xmlSchemaParserCtxtPtr));
   const char *url = wrenGetSlotString(vm, 1);
   *pctxt = xmlSchemaNewParserCtxt(url);

}

void C_xmlSchemaValidCtxtPtrAllocate(WrenVM* vm) {

   xmlSchemaValidCtxtPtr* pctxt = (xmlSchemaValidCtxtPtr*)wrenSetSlotNewForeign(vm, 0, 0, sizeof(xmlSchemaValidCtxtPtr));
   xmlSchemaPtr schema = *(xmlSchemaPtr*)wrenGetSlotForeign(vm, 1);
   *pctxt = xmlSchemaNewValidCtxt(schema);

}

void C_parse(WrenVM* vm) {

   xmlSchemaParserCtxtPtr ctxt = *(xmlSchemaParserCtxtPtr*)wrenGetSlotForeign(vm, 0);
   xmlSchemaPtr* pschema = (xmlSchemaPtr*)wrenGetSlotForeign(vm, 1);
   *pschema = xmlSchemaParse(ctxt);

}

void C_freeParserCtxt(WrenVM* vm) {

   xmlSchemaParserCtxtPtr ctxt = *(xmlSchemaParserCtxtPtr*)wrenGetSlotForeign(vm, 0);
   xmlSchemaFreeParserCtxt(ctxt);

}

void C_validateFile(WrenVM* vm) {

   xmlSchemaValidCtxtPtr ctxt = *(xmlSchemaValidCtxtPtr*)wrenGetSlotForeign(vm, 0);
   const char *filename = wrenGetSlotString(vm, 1);
   int options = (int)wrenGetSlotDouble(vm, 2);
   int res = xmlSchemaValidateFile(ctxt, filename, options);
   wrenSetSlotDouble(vm, 0, (double)res);

}

void C_freeValidCtxt(WrenVM* vm) {

   xmlSchemaValidCtxtPtr ctxt = *(xmlSchemaValidCtxtPtr*)wrenGetSlotForeign(vm, 0);
   xmlSchemaFreeValidCtxt(ctxt);

}

WrenForeignClassMethods bindForeignClass(WrenVM* vm, const char* module, const char* className) {

   WrenForeignClassMethods methods;
   methods.allocate = NULL;
   methods.finalize = NULL;
   if (strcmp(module, "main") == 0) {
       if (strcmp(className, "XmlSchemaPtr") == 0) {
           methods.allocate = C_xmlSchemaPtrAllocate;
       } else if (strcmp(className, "XmlSchemaParserCtxtPtr") == 0) {
           methods.allocate = C_xmlSchemaParserCtxtPtrAllocate;
       } else if (strcmp(className, "XmlSchemaValidCtxtPtr") == 0) {
           methods.allocate = C_xmlSchemaValidCtxtPtrAllocate;
       }
   }
   return methods;

}

WrenForeignMethodFn bindForeignMethod(

   WrenVM* vm,
   const char* module,
   const char* className,
   bool isStatic,
   const char* signature) {
   if (strcmp(module, "main") == 0) {
       if (strcmp(className, "Args") == 0) {
           if (isStatic && strcmp(signature, "xmlFilename") == 0) return C_xmlFilename;
           if (isStatic && strcmp(signature, "xsdFilename") == 0) return C_xsdFilename;
       } else if (strcmp(className, "XmlSchemaParserCtxtPtr") == 0) {
           if (!isStatic && strcmp(signature, "parse(_)") == 0) return C_parse;
           if (!isStatic && strcmp(signature, "freeParserCtxt()") == 0) return C_freeParserCtxt;
       } else if (strcmp(className, "XmlSchemaValidCtxtPtr") == 0) {
           if (!isStatic && strcmp(signature, "validateFile(_,_)") == 0) return C_validateFile;
           if (!isStatic && strcmp(signature, "freeValidCtxt()") == 0) return C_freeValidCtxt;
       }
   }
   return NULL;

}

static void writeFn(WrenVM* vm, const char* text) {

   printf("%s", text);

}

void errorFn(WrenVM* vm, WrenErrorType errorType, const char* module, const int line, const char* msg) {

   switch (errorType) {
       case WREN_ERROR_COMPILE:
           printf("[%s line %d] [Error] %s\n", module, line, msg);
           break;
       case WREN_ERROR_STACK_TRACE:
           printf("[%s line %d] in %s\n", module, line, msg);
           break;
       case WREN_ERROR_RUNTIME:
           printf("[Runtime Error] %s\n", msg);
           break;
   }

}

char *readFile(const char *fileName) {

   FILE *f = fopen(fileName, "r");
   fseek(f, 0, SEEK_END);
   long fsize = ftell(f);
   rewind(f);
   char *script = malloc(fsize + 1);
   fread(script, 1, fsize, f);
   fclose(f);
   script[fsize] = 0;
   return script;

}

int main(int argc, char **argv) {

   if (argc <= 2) {

printf("Usage: %s <XML Document Name> <XSD Document Name>\n", argv[0]); return 0; }

   xmlFilename = argv[1];
   xsdFilename = argv[2];
   WrenConfiguration config;
   wrenInitConfiguration(&config);
   config.writeFn = &writeFn;
   config.errorFn = &errorFn;
   config.bindForeignClassFn = &bindForeignClass;
   config.bindForeignMethodFn = &bindForeignMethod;
   WrenVM* vm = wrenNewVM(&config);
   const char* module = "main";
   const char* fileName = "xml_validation.wren";
   char *script = readFile(fileName);
   WrenInterpretResult result = wrenInterpret(vm, module, script);
   switch (result) {
       case WREN_RESULT_COMPILE_ERROR:
           printf("Compile Error!\n");
           break;
       case WREN_RESULT_RUNTIME_ERROR:
           printf("Runtime Error!\n");
           break;
       case WREN_RESULT_SUCCESS:
           break;
   }
   wrenFreeVM(vm);
   free(script);
   return 0;

}</lang>

Output:

Using command ./xml_validation shiporder.xml shiporder.xsd:

'shiporder.xml' validates.

Using a modified file “shiporder1.xml” where tag “orderperson” has been replaced by “orderperson1”:

Entity: line 6: Schemas validity error : Element 'orderperson1': This element is not expected. Expected is ( orderperson ).
'shiporder1.xml' fails to validate.