3
$\begingroup$

I am planning to write a package to read data which is saved in XML format that should follow some XML schema. As part of the reading I would like to do error checking, namely schema validation. So is there an idiomatic way of doing this in the Wolfram language? I was trying to avoid using external programs.

$\endgroup$
  • $\begingroup$ Can you provide an example of the kind of validation you are trying to do? Generally speaking Wolfram Language is well suited to handling XML thanks to its "symbolic XML" representation. $\endgroup$ – C. E. Apr 9 '16 at 0:58
  • $\begingroup$ Sure. Here is an example xml (example.xml): pastebin.com/GJqUDcas; here the corresponding xml schema (schema.xsd): pastebin.com/HZg2SdWs. From the command line I can then validate by: xml val -e --xsd schema.xsd example.xml, where xml here is a command from the XMLStarlet Command Line XML Toolkit. $\endgroup$ – rmagno Apr 9 '16 at 1:26
  • $\begingroup$ @paideia what is wrong with running such from the command line using Import["!"] or Run[] $\endgroup$ – William Apr 9 '16 at 19:38
  • $\begingroup$ @William, the only problem with using Run[] is that if I want this functionality to be in a package so that other people using the package would not need to have this dependency. I was lokking for a solution that would only need Mathematica. $\endgroup$ – rmagno Apr 9 '16 at 21:32
  • $\begingroup$ @paideia does the posted answer suit your needs? $\endgroup$ – William Apr 9 '16 at 23:10
8
$\begingroup$

JLink`

Here is a solution that uses JLink` to leverage Java's support of XML Schema:

Needs["JLink`"]
InstallJava[];

LoadJavaClass["javax.xml.validation.SchemaFactory", AllowShortContext -> False];
LoadJavaClass["javax.xml.XMLConstants", AllowShortContext -> False];

validateXml[xsd_String, xml_String] :=
  JavaBlock @ Module[{factory, xsdSource, xmlSource, schema, validator, valid, error}
  , xsdSource = stringSource[xsd]
  ; xmlSource = stringSource[xml]
  ; factory = javax`xml`validation`SchemaFactory`newInstance @
                javax`xml`XMLConstants`W3CUXMLUSCHEMAUNSUURI
  ; schema = factory@newSchema[xsdSource]
  ; validator = schema@newValidator[]
  ; error = ""
  ; Block[{$JavaExceptionHandler = (error = GetJavaException[]@getMessage[])&}
    , validator@validate[xmlSource]
    ; <| "valid" -> error === "", "error" -> error |>
    ]
]

stringSource[s_String] :=
  JavaNew[
    "javax.xml.transform.stream.StreamSource"
  , JavaNew["java.io.StringReader", s]
  ]

Here it is in action, using the sample files mention in the question's comments:

$xsd = Import["http://pastebin.com/raw/HZg2SdWs", "Text"];

$xml = Import["http://pastebin.com/raw/GJqUDcas", "Text"];

validateXml[$xsd, $xml]
(*
   <| "valid" -> True
    , "error" -> ""
    |>
*)

validateXml[$xsd, "<zot id='wrong'/>"]
(*
   <| "valid" -> "False"
    , "error" -> "cvc-elt.1: Cannot find the declaration of element 'zot'."
    |>
*)

validateXml[$xsd, StringReplace[$xml, "type>" -> "bad>"]]
(*
   <| "valid" -> False
    , "error" -> 
      "cvc-complex-type.2.4.a: Invalid content was found starting with element 'bad'.
       One of
       '{\"http://www.rdml.org\":description, \
         \"http://www.rdml.org\":documentation,
         \"http://www.rdml.org\":xRef, \
         \"http://www.rdml.org\":annotation,
         \"http://www.rdml.org\":type}' is expected."
    |>
*)

XMLSchema` Package

Incidentally, there is also a package called XMLSchema` that is used internally by Mathematica's web service implementation. Being undocumented, there is no reason to expect it to be applicable to our needs. Furthermore, I have found XML Schema support in Mathematica's WSDL functionality to be somewhat incomplete. So perhaps we should not be surprised by the following (unsanctioned) exchange:

Needs["XMLSchema`"]

$xsdObject = Import["http://pastebin.com/raw/HZg2SdWs", {"XML", "XMLObject"}];

LoadSchema[$xsdObject, "MySchema`"]

(* LoadSchema::typename: The global type <<1>> does not have a name. *)
| improve this answer | |
$\endgroup$
  • $\begingroup$ Thanks @WReach for this very usefull functions. However, when I copy-paste into my Mathematica 8.0, I get a syntax error <code>Syntax::sntxf: "validator@validate[xmlSource];<" cannot be followed by "|valid". </code>. Is the notation <| a new notation in Mathematica? Can it be written differently? Thanks. $\endgroup$ – Denis Cousineau Sep 7 '16 at 1:34
  • $\begingroup$ @DenisCousineau Association syntax was added in version 10. Prior to that, simply replace <| ... |> with { ... }. The parsing results will then be a list of rules instead of an association. $\endgroup$ – WReach Sep 7 '16 at 23:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.