Writing XML Output from Objects: Basics
This topic provides basic information on writing XML output from InterSystems IRIS® data platform objects, for arbitrary data, or a combination. The topic Writing XML Output from Objects: Details provides more detailed information.
Also see Writing XML Output from a DOM for an alternative technique.
Overview
To write XML:
-
If you need output for a particular object, the class definition for that object must be XML-enabled. With a few exceptions, the classes to which that object refers must also extend %XML.AdaptorOpens in a new tab.
-
Create an instance of %XML.WriterOpens in a new tab and then call methods of that instance. Your code will specify the overall structure of the XML output: the character encoding, the order in which objects are presented, whether processing instructions are included, and so on.
The following Terminal session shows a simple example that refers to a sample XML-enabled class:
GXML>Set p=##class(GXML.Person).%OpenId(1)
GXML>Set w=##class(%XML.Writer).%New()
GXML>Set w.Indent=1
GXML>Set status=w.RootObject(p)
<?xml version="1.0" encoding="UTF-8"?>
<Person GroupID="R9685">
<Name>Zimmerman,Jeff R.</Name>
<DOB>1961-10-03</DOB>
<Address>
<City>Islip</City>
<Zip>15020</Zip>
</Address>
<Doctors>
<Doctor>
<Name>Sorenson,Chad A.</Name>
</Doctor>
<Doctor>
<Name>Sorenson,Chad A.</Name>
</Doctor>
<Doctor>
<Name>Uberoth,Roger C.</Name>
</Doctor>
</Doctors>
</Person>
Overall Structure
Your code should do some or all of the following, in this order:
-
If you are working with an object that might be invalid, call the %ValidateObject() method of that object and check the returned status. If the object is not valid, the XML would also not be valid.
%XML.WriterOpens in a new tab does not validate the object before exporting it. This means that if you have just created an object and have not yet validated it, the object (and hence the XML) could be invalid (because, for example, a required property is missing).
-
Create an instance of the %XML.WriterOpens in a new tab class and optionally set its properties.
In particular, you might want to set the following properties:
-
Indent — Controls whether the output is generated within indentation and line wrapping (if Indent equals 1) or as a single long line (if Indent equals 0). The latter is the default. See Details on Indent Option.
-
IndentChars — Specifies the characters used for indentation. The default is a string of two spaces. This property has no effect if Indent is 0.
-
Charset — Specifies the character set to use. See Specifying the Character Set of the Output.
For readability, the examples in this documentation use Indent equal to 1.
Also see Properties That Affect the Prolog and Other Properties of the Writer.
-
-
Specify an output destination.
By default, output is written to the current device. To specify the output destination, call one of the following methods before starting to write the document:
-
OutputToDevice() — Directs the output to the current device.
-
OutputToFile() — Directs the output to the specified file. You can specify an absolute path or a relative path. Note that the directory path must already exist.
-
OutputToString() — Directs the output to a string. Later you can use another method to retrieve this string.
-
OutputToStream() — Directs the output to the specified stream.
-
-
Start the document. You can use the StartDocument() method. Note that the following methods implicitly start a document if you have not started a document via StartDocument(): Write(), WriteDocType(), RootElement(), WriteComment(), and WriteProcessingInstruction().
-
Optionally write lines of the prolog of the document. You can use the following methods:
-
WriteDocType() — Writes the DOCTYPE declaration. See ee Generating a Document Type Declaration.
-
WriteProcessingInstruction() — Writes a processing instruction. See Writing Processing Instructions.
-
-
Optionally specify the default namespace. The writer uses this for classes that do not have a defined XML namespace. See Specifying the Default Namespace.
Optionally add namespace declarations to the root element. To do so, you can invoke several utility methods before starting the root element. See Adding Namespace Declarations.
-
Start the root element of the document. The details depend on whether the root element of that document corresponds to an InterSystems IRIS object. There are two possibilities:
-
The root element might correspond directly to an InterSystems IRIS object. This is typically the case if you are generating output for a single object.
In this case, you use the RootObject() method, which writes the specified XML-enabled object as the root element.
-
The root element might be merely a wrapper for a set of elements, and those elements are InterSystems IRIS objects.
In this case, you use the RootElement() method, which inserts the root-level element with the name you specify.
Also see Writing the Root Element.
-
-
If you used the RootElement() method, call methods to generate the output for one or more elements inside the root element. You can write any elements within the root element, with any order or logic you choose. There are several ways that you can write an individual element, and you can combine these techniques:
-
You can use the Object() method, which writes an XML-enabled object. You specify the name of this element, or you can use the default, which is defined by the object.
-
You can use the Element() method, which writes the start tag for an element, with the name you provide. Then you can write content, attributes, and child elements, by using methods such as WriteAttribute(), WriteChars(), WriteCData(), and others. A child element could be another Element() or could be an Object(). You use the EndElement() method to indicate the end of the element.
-
You can use the %XML.ElementOpens in a new tab class and construct an element manually.
See Constructing an Element Manually for further details.
-
-
If you used the RootElement() method, call the EndRootElement() method. This method closes the root element of the document and decreases the indentation (if any), as appropriate.
-
If you started the document with StartDocument(), call the EndDocument() method to close the document.
-
If you directed the output to a string, use the GetXMLString() method to retrieve that string.
There are many other possible organizations, but note that some methods can be called only in some contexts. Specifically, once you start a document, you cannot start another document until you end the first document. If you try to do so, the writer method returns the following status:
#6275: Cannot output a new XML document or change %XML.Writer properties
until the current document is completed.
The StartDocument() method explicitly starts a document. Other methods implicitly start a document: Write(), WriteDocType(), RootElement(), WriteComment(), and WriteProcessingInstruction().
The methods described here are designed to enable you to write specific units to the XML document, but in some cases, you may need more control. The %XML.WriterOpens in a new tab class provides an additional method, Write(), which you can use to write an arbitrary string into any place in the output.
Also, you can use the Reset() method to reinitialize the writer properties. This is useful if you have generated an XML document and want to generate another one without creating a new writer instance.
Error Checking
Most of the methods of %XML.WriterOpens in a new tab return a status. You should check the status after each step and quit if appropriate.
Inserting Comment Lines
You can use the WriteComment() method to insert a comment line. You can use this method anywhere in your document. If you have not yet started the XML document, this method implicitly starts the document.
Example
The following example generates XML output for a given XML-enabled object.
ClassMethod Write(obj) As %Status
{
set writer=##class(%XML.Writer).%New()
set writer.Indent=1
//these steps are not really needed because
//this is the default destination
set status=writer.OutputToDevice()
if $$$ISERR(status) {
do $System.Status.DisplayError(status)
quit $$$ERROR($$$GeneralError, "Output destination not valid")
}
set status=writer.RootObject(obj)
if $$$ISERR(status) {
do $System.Status.DisplayError(status)
quit $$$ERROR($$$GeneralError, "Error writing root object")
}
quit status
}
Notice that this example uses the OutputToDevice() method to direct output to the current device, which is the default destination.
Suppose that we execute this method as follows:
set obj=##class(GXML.Person4).%OpenId(1)
do ##class(MyWriters.BasicWriter).Write(obj)
The output depends on what class is used, of course, but could look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<Person4>
<Name>Tesla,Alexandra L.</Name>
<DOB>1983-11-16</DOB>
<GroupID>Y9910</GroupID>
<Address>
<City>Albany</City>
<Zip>24450</Zip>
</Address>
<Doctors>
<Doc>
<Name>Schulte,Frances T.</Name>
</Doc>
<Doc>
<Name>Smith,Albert M.</Name>
</Doc>
</Doctors>
</Person4>
Details on Indent Option
You can use the Indent property of your writer to get output that contains additional line breaks, for greater readability. There is no formal specification for this formatting. This section describes the rules used by %XML.WriterOpens in a new tab if Indent equals 1:
-
Any element that contains only whitespace characters is converted to an empty element.
-
Each element is placed on its own line.
-
If an element is a child of a preceding element, the element is indented relative to that parent element. The indentation is determined by the IndentChars property, which defaults to two spaces.