Although Natural provides (very basic) functionality to parse and load an XML file, I could not find a way to validate XML against a schema. So I wrote a small Java program that does the validation and can be called from Natural (on Linux) via a User Exit.
Here is the Natural code for subprogram XMLVALID. It validates an XML file against a given schema using User Exit USR1052N (call a shell command) redirecting the output to a temp file. If an error occurred it reads the temp file and returns the error message. It uses quite a few hard coded paths you may need to change on your system.
DEFINE DATA * PARAMETER USING XMLVALP * LOCAL USING XMLVALC * LOCAL 01 USR1052L 02 OS-COMMAND (A253) 02 RESPONSE (I04) * 01 #TEMPFILE (A) DYNAMIC * 01 #WORK (A1000) * 01 #DEFAULT-SCHEMA-PATH (A) DYNAMIC CONST <'/home/XML/'> 01 #INDEX (N4) * END-DEFINE * RESET XMLVALP-DEFAULT * EXAMINE FULL XML-SCHEMA FOR '/' GIVING POSITION IN #INDEX IF #INDEX LE 0 COMPRESS #DEFAULT-SCHEMA-PATH XML-SCHEMA INTO XML-SCHEMA LEAVING NO END-IF * COMPRESS '/tmp/XML-Validation-' *DATN '-' *TIMN '.txt' INTO #TEMPFILE LEAVING NO COMPRESS '/usr/bin/java -cp' #DEFAULT-SCHEMA-PATH 'XMLValidator' XML-FILE XML-SCHEMA '>' #TEMPFILE INTO OS-COMMAND * CALLNAT 'USR1052N' USR1052L XMLVALP.XML-RESULT := RESPONSE * IF XMLVALP.XML-RESULT NE VALID-XML DEFINE WORK FILE 1 #TEMPFILE TYPE 'ASCII' READ WORK 1 #WORK COMPRESS XML-ERROR #WORK INTO XMLVALP.XML-ERROR END-WORK CLOSE WORK 1 END-IF * COMPRESS 'rm' #TEMPFILE INTO OS-COMMAND CALLNAT 'USR1052N' USR1052L * END
The Java validator is pretty straightforward:
package xml;
import java.io.File;
import java.io.IOException;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.w3c.dom.Document;
import org.xml.sax.SAXParseException;
public class XMLValidator
{
private final static int INVALID_PARAMETERS = 1;
private final static int INVALID_XML = 2;
private final static int INVALID_FILE = 3;
private final static int ANY_ERROR = 4;
public static void main(String[] args)
{
if (args.length != 2)
{
System.out.println("Usage: XMLValidator PathToXMLFile PathToSchemaFile");
System.exit(INVALID_PARAMETERS);
}
String xmlFile = args[0];
String xsdFile = args[1];
try
{
validateXml(xmlFile, xsdFile);
}
catch (Exception e)
{
System.out.println("Error: " + e.getMessage());
System.exit(ANY_ERROR);
}
}
private static void validateXml(String xmlFile, String xsdFile) throws Exception
{
// create a factory that understands namespaces and validates the XML input
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
try
{
// read the XML file
DocumentBuilder builder = factory.newDocumentBuilder();
Document mDoc = builder.parse(new File(xmlFile));
// create a SchemaFactory capable of understanding schemas
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// load the schema
Schema schema = schemaFactory.newSchema(new File(xsdFile));
// validate the XML file
Validator validator = schema.newValidator();
validator.validate(new DOMSource(mDoc));
}
catch (SAXParseException spe)
{
// error generated by the parser
System.out.println("Parser error: URI " + spe.getSystemId() + ", Line " + spe.getLineNumber() + ", Message: " + spe.getMessage());
System.exit(INVALID_XML);
}
catch (IOException ioe)
{
System.out.println("Error reading file '" + xmlFile + "'.");
System.exit(INVALID_FILE);
}
}
}