14. Jess and XML
14.1. Introduction
Jess now supports an XML-based rule language. Rather than adopting an evolving standard like RuleML, Jess's XML rule language (JessML) was designed to be close in structure to the Jess language itself. JessML is a hybrid imperative/declarative language, just like the Jess language itself. That means you can not only write rules in it, but define and call functions as well. Anything you can do in the Jess language, you can do in JessML too.
Transformation between JessML and RuleML and other XML rule languages should be simple using XSLT.
JessML code is quite verbose. Remember, though, that's it's intended to be easy for machines, not humans, to work with.
14.2. The JessML Language
Jess comes with an XML schema for JessML; it's provided as the file JessML1_0.xsd in the lib subdirectory of the Jess distribution. The schema as written accepts all well-formed JessML code, but does not reject all invalid code; it does not include all the semantics of the JessML language (for example, the requirement that a template be defined before it is used in a pattern.) The JessML namespace URI is http://www.jessrules.com/JessML/1.0 and should be included in all JessML documents. A well-formed JessML document should therefore be of the form
<?xml version='1.0' encoding='US-ASCII'?> <rulebase xmlns='http://www.jessrules.com/JessML/1.0'> ... </rulebase>
14.2.1. The rulebase Element
The top-level element in an executable JessML file is the rulebase element. A rulebase element can contain any other JessML elements in any order.14.2.2. Names
Many constructs -- rules, templates, modules, functions -- have a name. In JessML, names are represented by a name element. A name element contains the text of the name, optionally including a module name:- PLANNING::store-plan-data
- MAIN::animal
- make-pair
14.2.3. Values
Just as in Jess, simple data items in JessML are called values. A JessML value consists of a type attribute and the data itself. The type attribute can contain one of the type names in the jess.RU class. Here are a few examples of valid values:- <value type="SYMBOL">nil</value>
- <value type="INTEGER">1</value>
- <value type="STRING">A String</value>
14.2.4. Function calls
The actions of rules, some of their tests and the bodies of deffunctions are made up of funcalls, short for "function calls." A JessML funcall tag contains a name element and a series of value elements as the arguments. The Jess function call "(+ ?x 1)" looks like this in JessML:<funcall> <name> + </name> <value type="VARIABLE">x</value> <value type="INTEGER">1</value> </funcall>
14.2.4.1. Special functions
A few special functions have slightly different syntax. The modify and duplicate functions each accept a single value plus one or more slot elements as arguments, while the assert function accepts one or more fact elements.14.2.5. Slots
A slot in JessML is just the same as it is in Jess: it's a list (usually a two-element list, but sometimes longer) with a symbol as the head. In JessML, a slot element always starts with a name element, and contains one or more value elements; for example,<slot> <name> location </name> <value type="SYMBOL">nil</value> </slot>
14.2.6. Templates
You can define a template in the JessML language using the template element. A template element contains a name element followed by any number of elements. The value elements of the slots are interpreted as default values.
<template> <name> monkey </name> <slot> <name> species </name> <value type="SYMBOL"> rhesus </value> </slot> </template>
You can also automatically define a template based on a Java class using a from-class property, like this:
<template> <name> MAIN::button </name> <properties> <property> <name>from-class</name> <value type="SYMBOL">java.awt.Button</value></property> </properties> </template>
14.2.7. Rules
A rule contains a nameelement, a lhs element, and a rhs element. The rhs element contains a collection of funcall elements representing the rule's actions. The lhs element contains groups and patterns, which are somewhat complex, and will be discussed in the following sections. A simple rule might look like this (this rule prints "Fired!" for each "MAIN::button" fact that exists.
<rule> <name> MAIN::myrule </name> <lhs> <group> <name> and </name> <pattern> <name> MAIN::button </name> </pattern> </group> </lhs> <rhs> <funcall> <name> printout </name> <value type="SYMBOL">t</value> <value type="STRING">Fired!</value> <value type="SYMBOL">crlf</value> </funcall> </rhs> </rule>
14.2.7.1. Groups
A group element represents one of Jess's grouping conditional elements: and, or, or not. Every rule has an "and" group enclosing all of its patterns, as in the example rule above. A group always contains a name element followed by any combination of nested groups or pattern elements.14.2.7.2. Patterns
A pattern element represents a single pattern in a rule. It consists of a name element, an optional binding element, and any number of slot elements. The values of the slots should consist of test elements (described below.)14.2.7.3. Pattern bindings
You can use a binding element to bind a variable to the fact that matches a pattern. The text of the binding element is simply the name of the variable to use.
14.2.7.4. Tests
A test element represents a single test in a pattern. A test can either be discriminating or nondiscriminating. A discriminating test is one that sometimes matches and sometimes doesn't. A nondiscriminating test is one that always matches; generally this refers to tests that merely bind a variable to the contents of a slot. There's no difference in the syntax between these two types of test.
A test element contains a type element, an optional conjunction element, and either a value element or a funcall element. The type element's body is either "eq" or "neq", corresponding to a positive or negative test. The conjunction element's body can be either "and" or "or", and it is can used only in the second and later tests on a slot. Finally, the value element represents the test itself. If it's a constant, the contents of the slot will be compared to it for equality or inequality, according to the contents of the type element. If the value is a variable, and if that variable has not been bound to a slot, the variable is bound to the current slot. Otherwise, the variable is compared to the contents of the slot. Finally, if there is a funcall element, the function is evaluated and the test passes if the result is true (for an "eq" test) or false (for a "neq" test.)
14.2.8. Fact files
The save-facts-xml function writes an XML-formatted file containing only Jess facts. The load-facts function knows how to read those files; the facts contained in the file will be immediately asserted. Any deftemplates needed must have been previously defined or an error will occur. You can create this kind of file yourself, as well. Such a file should be a complete, well-formed XML file containing an XML declaration and a fact-list root element. Inside the document should be nothing but fact elements, each representing a single Jess fact.
14.3. Writing Constructs in JessML
You can translate a file of Jess language code into XML using Jess's jess.xml.XMLPrinter class; this is a good way to learn how Jess and JessML correspond. You can use this tool from the command line like this:
java -classpath jess.jar jess.xml.XMLPrinter myfile.clp > myfile.xml
The file "myfile.xml" will be a complete translation of all the code in "myfile.clp", including templates, rules, deffunctions, and top-level function calls.
The XMLPrinter class and its companion jess.xml.XMLVisitor can also be used to translate Jess code into XML programatically. You could use XMLPrinter to translate a file of Java code like this:
import jess.xml.XMLPrinter; import jess.JessException; import java.io.*; public class ExXMLPrinter { public static void main(String[] args) throws JessException, IOException { FileReader fr = new FileReader(args[0]); XMLPrinter printer = new XMLPrinter(new PrintWriter(System.out, true)); try { printer.printFrontMatter(); printer.translateToXML(fr); printer.printBackMatter(); } finally { printer.close(); } } }
To translate individual constructs and other Jess objects that you've already read into Jess, you can use the XMLVisitor class. To write a single Jess object as XML, you create an XMLVisitor instance, passing the object as the constructor argument; any of the library classes that implement jess.Visitable will do. Then you can just call jess.XMLVisitor.toString() on this instance to get the XML version.
import jess.xml.*; import jess.*; import java.io.*; public class ExXMLVisitor { public static void main(String[] argv) throws JessException { Rete engine = new Rete(); engine.eval("(defrule hello => )"); XMLVisitor visitor = new XMLVisitor(engine.findDefrule("hello")); System.out.println(visitor); } }
Jess comes with an XML Schema which you can use to validate the XML you write; it's the file JessML1_0.xsd in the lib subdirectory. Jess won't actually validate your code against this schema, as the standard JDK XML parser is a non-validating parser. To validate your code you'll need a validating parser like Apache Xerces.
14.4. Parsing JessML
In general, anywhere you can use a file full of Jess language code, you can also use a JessML file. For example, the file you provide as a command-line argument to jess.Main can be a JessML file.
The standard batch function can read JessML code as well as Jess language code. It decides what kind of file it is looking at by looking for the "<?xml" characters that should begin a well-formed XML file. require, which uses batch internally, will work with JessML as well.
You can get a little bit more control of the process by using the classes JessSAXParser or JessSAXHandler directly. JessSAXParser will parse a JessML document from an InputSource, installing the results into a Rete object you provide. JessSAXHandler is a SAX event handler that you can use with your own parser.