diff --git a/README.md b/README.md index e051d86..e72acc1 100644 --- a/README.md +++ b/README.md @@ -315,3 +315,17 @@ save_file(xdoc, filename) # save xdoc to an XML file string(xdoc) # formatted XML doc to a string show(io, xdoc) # output formatted XML document ``` + +##### Functions to validate a document + +```julia +xsd = XMLSchema(url) # parse an XSD schema file or URL + +isvalid = validate(xmlfile, schema) # validate an XML file against a previously loaded XSD schema +isvalid = validate(doc, schema) # validate a LightXML XML Document against a previously loaded XSD schema +isvalid = validate(url, schema) # validate a URI or file against an XSD Schema document +isvalid = validate(element, schema) # validate a LightXML XML Node (a subtree) against an XSD Schema document +isvalid = validate(xmlfile, schemafile) # validate an XML file or URL against a XSD schem file or URL +``` + + diff --git a/src/LightXML.jl b/src/LightXML.jl index 823895a..a84b13f 100644 --- a/src/LightXML.jl +++ b/src/LightXML.jl @@ -19,7 +19,10 @@ export # document XMLDocument, version, encoding, compression, standalone, root, - parse_file, parse_string, save_file, set_root, create_root + parse_file, parse_string, save_file, set_root, create_root, + + # schema + XMLSchema, validate const Xchar = UInt8 const Xstr = Ptr{Xchar} @@ -43,5 +46,6 @@ include("utils.jl") include("nodes.jl") include("document.jl") include("cdata.jl") +include("schema.jl") end diff --git a/src/errors.jl b/src/errors.jl index af47540..7d5c7f3 100644 --- a/src/errors.jl +++ b/src/errors.jl @@ -15,3 +15,7 @@ end struct XMLTreeError{T<:AbstractString} <: XMLError msg::T end + +struct XMLValidationError{T<:AbstractString} <: XMLError + msg::T +end diff --git a/src/schema.jl b/src/schema.jl new file mode 100644 index 0000000..2bda3d6 --- /dev/null +++ b/src/schema.jl @@ -0,0 +1,89 @@ +""" +An XML Schema Document, produced by an XML file or XMLDocument that is XML for the schema. +""" +mutable struct XMLSchema + ptr::Xptr + function XMLSchema(ctxt::Xptr) + schema = ccall((:xmlSchemaParse, libxml2), Xptr, (Xptr,), ctxt) + schema != C_NULL || throw(XMLValidationError("Bad XML Schema in Document")) + ccall((:xmlSchemaFreeParserCtxt, libxml2), Cvoid, (Xptr,), ctxt) + obj = new(schema) + finalizer(x -> Libc.free(x.ptr), obj) + end +end + +""" +Create an XMLSchema from a file or url +""" +function XMLSchema(url::String) + ctxt = ccall((:xmlSchemaNewParserCtxt, libxml2), Xptr, (Cstring,), url) + ctxt != C_NULL || throw(XMLValidationError("Bad XML Schema at " * url)) + return XMLSchema(ctxt) +end + +""" +Create an XMLSchema from an XMLDocument +""" +function XMLSchema(doc::XMLDocument) + ctxt = ccall((:xmlSchemaNewDocParserCtxt, libxml2), Xptr, (Xptr,), doc.ptr) + ctxt != C_NULL || throw(XMLValidationError("Bad XML Schema in Document")) + return XMLSchema(ctxt) +end + +""" +Use an existing XMLschema to validate +""" +function _schema_valid_ctxt(f::Function, schema::XMLSchema) + ctxt = ccall((:xmlSchemaNewValidCtxt, libxml2), Xptr, (Xptr,), schema.ptr) + err = try + f(ctxt) + finally + Libc.free(ctxt) + end + return err +end + +""" +Validate an XMLDocument with an XMLSchema +Returns true if valid +""" +function validate(xml::XMLDocument, schema::XMLSchema) + err = _schema_valid_ctxt(schema) do ctxt + ccall((:xmlSchemaValidateDoc, libxml2), + Cint, (Xptr, Xptr), ctxt, xml.ptr) + end + return err == 0 +end + +""" +Validate an XML file or url with an XMLSchema +Returns true if valid +""" +function validate(url::String, schema::XMLSchema) + err = _schema_valid_ctxt(schema) do ctxt + ccall((:xmlSchemaValidateFile, libxml2), + Cint, (Xptr, Cstring), ctxt, url) + end + return err == 0 +end + +""" +Validate an XMLElement of an XMLDocument with an XMLSchema +Returns true if valid +""" +function validate(elem::XMLElement, schema::XMLSchema) + err = _schema_valid_ctxt(schema) do ctxt + ccall((:xmlSchemaValidateOneElement, libxml2), + Cint, (Xptr, Xptr), ctxt, elem.node.ptr) + end + return err == 0 +end + +""" +Validate an XML file or url with an XSD file or url +Returns true if valid +""" +function validate(url::String, schemafile::String) + schema = XMLSchema(schemafile) + return validate(url, schema) +end diff --git a/test/invalid.xml b/test/invalid.xml new file mode 100644 index 0000000..57970dc --- /dev/null +++ b/test/invalid.xml @@ -0,0 +1,24 @@ + + + + John Smith + + Ola Nordmann +
Langgt 23
+ 4000 Stavanger + Norway +
+ + Empire Burlesque, Gold Ribbon + Special Edition + 1 + 10.90 + + + Hide your heart + 1 + 9.90 + +
diff --git a/test/runtests.jl b/test/runtests.jl index d7f2b6f..9ff12a4 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,7 +1,7 @@ using LightXML using Test -tests = ["parse", "create", "cdata", "pi"] +tests = ["parse", "create", "cdata", "pi", "validate"] for t in tests fpath = "$t.jl" diff --git a/test/valid.xml b/test/valid.xml new file mode 100644 index 0000000..7a6140b --- /dev/null +++ b/test/valid.xml @@ -0,0 +1,24 @@ + + + + John Smith + + Ola Nordmann +
Langgt 23
+ 4000 Stavanger + Norway +
+ + Empire Burlesque + Special Edition + 1 + 10.90 + + + Hide your heart + 1 + 9.90 + +
diff --git a/test/valid.xsd b/test/valid.xsd new file mode 100644 index 0000000..5209496 --- /dev/null +++ b/test/valid.xsd @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/validate.jl b/test/validate.jl new file mode 100644 index 0000000..55d56c6 --- /dev/null +++ b/test/validate.jl @@ -0,0 +1,16 @@ +@testset "XML Validation with XSD" begin + + @test validate("valid.xml", "valid.xsd") + @test validate("invalid.xml", "valid.xsd") == false + + doc = parse_file("valid.xml") + schema = XMLSchema("valid.xsd") + + @test validate("valid.xml", schema) + @test validate("invalid.xml", schema) == false + + @test validate(doc, schema) + + @test validate(root(doc), schema) + +end