JMRI uses XML for a number of purposes: to hold decoder definitions, for its persistance system for configuration and panel information, and to create parts of the website from other files. This page describes how we specify the content of those files using XML schema.
For examples (not a tutorial!) on the structure of our schema, see the examples page.
The current schema can be seen online in the schema directory. The most commonly used one is the layout.xsd schema for panel files. See below on how it's organized.
Those XML Schema may need to be available to the program when it reads the files, as they define the default values of missing attributes and other needed information.
In the JMRI distributions, these are stored in the /xml/schema/ directory. Note that they are not stored in each directory alongside the XML files. There are just too many locations to keep such a set of schema definition files up to date. JMRI itself, via the jmri.jmrit.XmlFile class, provides support for locating those files when the XML parser needs them.
Every time you change what JMRI writes (and therefore reads) in an XML file, you must also do the following:
Please don't skip the latter steps. They matter a lot to the long-term stability of JMRI code.
If possible, it's best to make changes by addition, so that existing files can continue to be read unchanged. JMRI strongly values backward compatibility, where any newer version of JMRI can still load and use a file written by an older version.
If you can make a change that's just an addition, then the process is:
xmllint -noout -schema http://www.w3.org/2001/XMLSchema.xsd myChangedFile.xsd
"Versioning" schema allows us to have different schema for older and newer files. This lets newer versions of JMRI continue to check and read files that were written by older versions of JMRI. This backward compatibility is an important JMRI feature which we don't want to lose.
In practical terms, versioning consists of having multiple-but-related versions of the schema definition files which are labeled by the first JMRI version that can read them.
When do you need to create a new version?
In that case, just make your schema changes in the current schema document, and commit them back to the JMRI code repository.
In this case, the steps to version the schema are:
src/jmri/configurexml/ConfigXmlManager.java
static final public String schemaVersion = "-3-7-3"
java/test/jmri/configurexml/loadref/BlockAndSignalMastTest.xml
<layout-config
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://jmri.org/xml/schema/layout-4-7-2.xsd">
Note that the unlabeled schema is the primordial, oldest, now-obsolete schema. For example layout.xsd is older than layout-2-9-6.xsd, and therefore not to be used for new files anymore. Don't assume that layout.xsd is the default for new files!
It's important that the JMRI schema definitions be kept semantically correct. If we let too many problems build up, we'll eventually have a lot of back-fitting to do. The W3C online schema validation tool is a good tool for checking that changes to the JMRI schema are still technically correct. You should check your changes with it before committing them to the repository. Unfortunately, it doesn't seem to check compliance with nested schema elements, e.g. from DocBook (see below) or JMRI schema, but it's still a very useful check.
Using the JMRI "Validate XML File" tool in the "Debug" menu to validate a .xml file ("instance file") that uses your new or updated schema is an important check of both. Use it often during development. You can also use it from the command line via e.g.:
./runtest.csh apps/jmrit/XmlFileValidateRunner xml/decoders/0NMRA.xml
There is also a PowerShell script available, see the JUnit page for more information.
For a quick file check, Linux and Mac OS X users can validate from the command line with e.g.
cd xml
xmllint -schema schema/aspecttable.xsd -noout signals/sample-aspects.xml
xmllint
can check schema files themselves if you specify
-schema http://www.w3.org/2001/XMLSchema.xsd
in the command.
Your schema docs should point to our standard stylesheet in their head matter:
<?xml-stylesheet href="schema2xhtml.xsl" type="text/xsl"?>
Stylesheets turn XML code, like this schema, into a human-readable form when the XML is
parsed and displayed by a browser. For an example of that, click on this link to the aspecttable.xsd schema file. Our standard
stylesheet is pretty basic. It just shows basic structure. If anybody knows of a better stylesheet,
we can certainly switch to it.
That's done by having a SchemaTest class in your test-tree package (see e.g. test/jmri/configurexml/SchemaTest.java for an example) that checks all the XML files kept there. If that's in place, just put a copy of a (new) typical XML file in the existing "valid" subdirectory.
To more extensively check your schema, you can check that it fails XML files that you think are not valid. There are lots of ways to be not valid, and you don't need to check them all, but if there something specific you want to be sure of, put an example of that in the "invalid" subdirectory. Those files are expected to fail for some specific reason. You should document that reason via comments in the file itself so your colleagues can figure it out later.
If there's no "valid" subdirectory, create one and add it to the end of the SchemaTest class in that package. If there's no SchemaTest class, create one by replicating an existing one, see link above.
That's done by having a LoadAndStoreTest class in your test-tree package (see e.g. test/jmri/configurexml/LoadAndStoreTest.java for an example) that checks all the XML files kept there. If that's in place, just put a copy of a (new) typical XML file in the existing "load" subdirectory.
If there's no "load" subdirectory, create one and add it to the end of the LoadAndStoreTest class in that package. If there's no LoadAndStoreTest class, create one by replicating an existing one, see link above.
When LoadAndStoreTest runs, it loads the files in the load directory one-by-one, storing each back out to the "temp" directory within the local preferences directory, and then compares the input and output files. Sometimes this load-and-store process results in something that's in a different order, or contains more info (e.g. attributes missing from the input file are written with default values in the output file). If the comparison fails, but the output file is still OK when you manually inspect it, copy that output file to the "loadref" directory (create it if needed) within your test package. See test/jmri/configurexml/loadref for an example. LoadAndStoreTest will compare to files it finds here, instead of to the original file in the "load" subdirectory.
If your changes to the code cause this testing to fail with older versions of the file, do not change the older file version. Instead, either put an updated output reference in the "loadref" directory, version the schema to allow the older file to continue to load, or improve your code. Backward compatibility is important!
Note: Do not remove or modify any existing XML file checks. Those keep old versions of the files working! If your new code and/or schema breaks the processing of the existing files, you need to either fix your code or version the schema to allow multiple formats to coexist.
Our preferred organization for XML schema is based on the structure of the underlying code: A particular *Xml class is the unit of reuse.
Lots of classes descend from jmri.configurexml.XmAdapter: (see Javadoc)
By convention, provide <xsd:annotation><xsd:appinfo> element containing the class name that reads/writes the element:
<xs:annotation>
<xs:documentation>
Some human readable docs go here
</xs:documentation>
<xs:appinfo>
<jmri:usingclass configurexml="false">jmri.managers.DefaultSignalSystemManager</jmri:usingclass>
</xs:appinfo>
</xs:annotation>
This limits the number of types, and keeps the schema files roughly aligned with the classes that do the reading and writing.
There are a few items (elements and attribute groups) that extend across multiple types. They are defined in the types/general.xsd file.
More information on XML schema design patterns is available at the Oracle Java site.
A partial list of the pre-defined types:
For various reasons, we are moving to DocBook format for Copyright, Author and Revision information in our XML files (instance files).
Sample XML:
<copyright>
<year>2009</year>
<year>2010</year>
<holder>JMRI</holder>
</copyright>
<authorgroup>
<author>
<personname>
<firstname>Sample</firstname>
<surname>Name</surname>
</personname>
<email>name@com.domain</email>
</author>
</authorgroup>
<revhistory>
<revision>
<revnumber>1</revnumber>
<date>2009-12-28</date>
<dauthorinitials>initials</authorinitials>
</revision>
</revhistory>
For an example, see the NMRA standard decoder definition in xml/decoders/0NMRA.xml.
The exact schema definition for this in schema/docbook
Learning to use these will require some work, as we can't assume that computers using JMRI have internet access, so can't just reference the entire schema as remote entities.