XML
Extensible Markup Language (XML) is a simple, very flexible text format derived from SGML (ISO 8879). Originally designed to meet the challenges of large-scale electronic publishing, XML is also playing an increasingly important role in the exchange of a wide variety of data on the Web and elsewhere.
XML is a markup language similar to HTML, but without predefined tags to use. Instead, you define your own tags designed specifically for your needs. This is a powerful way to store data in a format that can be stored, searched, and shared. Most importantly, since the fundamental format of XML is standardized, if you share or transmit XML across systems or platforms, either locally or over the internet, the recipient can still parse the data due to the standardized XML syntax.
Comparison between XML and HTML:
- XML was designed to carry data - with a focus on what data “is”
- HTML was designed to display data - with focus on how data “looks”
- XML tags are not predefined like HTML tags are
For more information regarding XML:
The following documentation details how XML content can be read and written using the described Omni Automation classes and methods.
XML.Document Class
Instances of the XML.Document class are created to hold XML data, enabling it to be parsed and edited. XML documents can be created from existing XML content, or constructed in sections by adding XM elements.
Class Functions
The XML.Document class functions can be used to create new XML documents from XML data read from files, the clipboard, or network servers.
fromData(data: Data, whitespaceBehavior: XML.WhitespaceBehavior or null) → (XML.Document) • Parse the given data as an XML document.
Constructors
Creating empty instances of the XML.Document that can then be populated with XML data as needed.
new XML.Document(rootElement: String or XML.Element, configuration: XML.Document.Configuration or null) → (XML.Document) • Returns a new XML.Document with the given root element and configuration.
Instance Functions
The functions that can be called on an instance of the XML.Document class:
xmlData() → (Data) • Encodes the document as XML.
addElement(name: String, function: Function() or null) → ( ) • Appends a new element with the given name. If a function is passed, it is pushed it on the current element stack, the supplied function is called, and then the element is popped off the stack.
appendString(string: String) → ( ) • Appends the given string as a child of topElement.
setAttribute(attribute: String, value: String or null) → ( ) • Sets the specified attribute on topElement.
Properties
The properties of an instance of the XML.Document class.
- Q: What is a DTD?
- A DTD is a Document Type Definition. A DTD defines the structure and the legal elements and attributes of an XML document.
dtdPublicID (String or null r/o) • The public identifier of a Notation; or null if no public identifier is specified.
dtdSystemID (URL or null r/o) • This property returns the system identifier of the external DTD.
rootElement (XML.Element r/o) • The main element of the document that contains the XML data as XML.Element instances
schemaID (URL or null r/o) • Returns the schema ID associated with a schema name.
schemaNamespace (String or null r/o) • Schemas are a collection of rules (also referred to as a grammar or vocabulary) that consist of type definitions (simple and complex), as well as element and attribute declarations. Because XML documents can have several vocabularies to describe different elements and attributes, the use of namespaces and prefixes removes ambiguity for element and attribute declarations. Distinguishing between element and attribute names for each namespace is essential when you use schemas from more than one namespace.
stringEncoding (StringEncoding r/o) • To avoid errors while working with XML it is necessary to specify the type of encoding or the XML file should be saved as Unicode.
topElement (XML.Element r/o) • Returns the element at the top of the current element stack. Intially this is the root element, but when addElement() is called, it is temporarily updated to the new element (possibly recursively).
whitespaceBehavior (XML.WhitespaceBehavior r/o) • The concept behind whitespace is very simple. It is simply the space between text, graphics, images, and blocks. Whitespace is also known as negative space or blank space. The value of this property indicates how the whitespace will be handled in the display of the XML data.
XML.Document.Configuration Class
Supporting class for creating an instance of the XML.Document clasd.
new XML.Document.Configuration() → (XML.Document.Configuration) • Returns a new XML.Document.Configuration with default settings.
The values of these properties can be set on the newly created XML.Document.Configuration instance:
dtdPublicID (String or null)
dtdSystemID (URL or null)
schemaID (URL or null)
schemaNamespace (String or null)
stringEncoding (StringEncoding)
whitespaceBehavior (XML.WhitespaceBehavior or null)
XML.WhitespaceBehavior Class
Used in the creation of an instance of XML.Document determines how whitespace is handled.
new new XML.WhitespaceBehavior(defaultBehavior: XML.WhitespaceBehavior.Type) → (XML.WhitespaceBehavior) • Returns a new XML.WhitespaceBehavior with the given default type.
setBehaviorForElementName(behavior: XML.WhitespaceBehavior.Type, elementName: String)
behaviorForElementName(elementName: String) → (XML.WhitespaceBehavior.Type)
XML.WhitespaceBehavior.Type Class
Auto (XML.WhitespaceBehavior.Type r/o)
Ignore (XML.WhitespaceBehavior.Type r/o)
Preserve (XML.WhitespaceBehavior.Type r/o)
all (Array of XML.WhitespaceBehavior.Type r/o) • This property is used when constructing form interfaces.
XML.WhitespaceBehavior.Type.Auto
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<main-element><make>Chevy</make><model>Bolt</model><color>Red</color></main-element>
XML.WhitespaceBehavior.Type.Ignore
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<main-element>
<make>Chevy</make>
<model>Bolt</model>
<color>Red</color>
</main-element>
XML.WhitespaceBehavior.Type.Preserve
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<main-element><make>Chevy</make><model>Bolt</model><color>Red</color></main-element>
StringEncoding Class
These are the properties of the StringEncoding class, whose value is expressed as dot-extensions of the parent class, such as: StringEncoding.UTF8
ASCII (StringEncoding r/o)
ISO2022JP (StringEncoding r/o)
ISOLatin1 (StringEncoding r/o)
ISOLatin2 (StringEncoding r/o)
JapaneseEUC (StringEncoding r/o)
MacOSRoman (StringEncoding r/o)
NextStep (StringEncoding r/o)
NonLossyASCII (StringEncoding r/o)
ShiftJIS (StringEncoding r/o)
Symbol (StringEncoding r/o)
UTF16 (StringEncoding r/o)
UTF16BigEndian (StringEncoding r/o)
UTF16LittleEndian (StringEncoding r/o)
UTF32 (StringEncoding r/o)
UTF32BigEndian (StringEncoding r/o)
UTF32LittleEndian (StringEncoding r/o)
UTF8 (StringEncoding r/o) Default Value
Unicode (StringEncoding r/o)
WindowsCP1250 (StringEncoding r/o)
WindowsCP1251 (StringEncoding r/o)
WindowsCP1252 (StringEncoding r/o)
WindowsCP1253 (StringEncoding r/o)
WindowsCP1254 (StringEncoding r/o)
all (Array of StringEncoding r/o) • This property is used when creating plug-in forms.
Create New XML Document
keys = ["make", "model", "color"]
values = ["Chevy", "Bolt", "Red"]
config = new XML.Document.Configuration()
behavior = new XML.WhitespaceBehavior(XML.WhitespaceBehavior.Type.Ignore)
config.whitespaceBehavior = behavior
doc = new XML.Document("main-element", config)
keys.forEach((key, index) => {
doc.addElement(key, function(){
doc.appendString(values[index])
})
})
xmlText = doc.xmlData().toString()
console.log(xmlText)
The resulting XML textual data:
Resulting XML Textual Data
<?xml version="1.0" encoding="UTF-8"?>
<main-element>
<make>Chevy</make>
<model>Bolt</model>
<color>Red</color>
</main-element>
Hers’s a variation of the previous script that creates an XML document that is saved to disk:
Create/Save XML File
(async () => {
// CREATE XML DOCUMENT
keys = ["make", "model", "color"]
values = ["Chevy", "Bolt", "Red"]
config = new XML.Document.Configuration()
behavior = new XML.WhitespaceBehavior(XML.WhitespaceBehavior.Type.Ignore)
config.whitespaceBehavior = behavior
doc = new XML.Document("main-element", config)
keys.forEach((key, index) => {
doc.addElement(key, function(){
doc.appendString(values[index])
})
})
// SAVE XML DOCUMENT TO DISK
wrapper = FileWrapper.withContents("Car-Data.xml", doc.xmlData())
filesaver = new FileSaver()
urlObj = await filesaver.show(wrapper)
new Alert("FILE URL", urlObj.string).show()
})();
The following example uses the documentsDirectory property of the URL class to write XML data to a file in the app’s default folder without requiring user-approval.
Writing XML Data to File without Picker
keys = ["name", "id", "rate"]
values = ["Samantha", "com.apple.speech.synthesis.voice.samantha.premium", "0.45"]
config = new XML.Document.Configuration()
behavior = new XML.WhitespaceBehavior(XML.WhitespaceBehavior.Type.Ignore)
config.whitespaceBehavior = behavior
doc = new XML.Document("main-element", config)
keys.forEach((key, index) => {
doc.addElement(key, function(){
doc.appendString(values[index])
})
})
wrapper = FileWrapper.withContents("voice-data.xml", doc.xmlData())
folderURL = URL.documentsDirectory
fileURL = folderURL.appendingPathComponent("voice-data.xml")
wrapper.write(fileURL, [FileWrapper.WritingOptions.Atomic], null)
if(Device.current.type === DeviceType.mac){folderURL.open()}
And here's how to read the XML data from the previous example:
Reading XML Data from File
folderURL = URL.documentsDirectory
fileURL = folderURL.appendingPathComponent("voice-data.xml")
fileURL.fetch(function(data){
doc = XML.Document.fromData(data)
voiceName = doc.rootElement.firstChildNamed("name").stringContents
voiceID = doc.rootElement.firstChildNamed("id").stringContents
voiceRate = doc.rootElement.firstChildNamed("rate").stringContents
console.log("Voice Name: ", voiceName)
console.log("Voice ID: ", voiceID)
console.log("Voice Rate: ", voiceRate)
}, function(err){
new Alert(err.name, err.message).show()
})
XML.Element Class
The XML.Element class is used to describe the fundamental component of XML data, the XML Element. As mentioned previously, XML elements, unlike those of HTML, are not predefined but are structured, stored, and retrieved in a consistent ways.
Constructors
Creating an instance of an XML.Element uses the standard new item constructor:
new XML.Element(name: String) → (XML.Element) • Returns a new XML.Element with the given name.
Instance Properties
Each instance of the XML.Element class has specific properties:
attributeCount (Number r/o) • Returns the number of attributes assigned to this element.
attributeNames (Array of String r/o) • Returns the names of the attributes in the order they were added to the element.
children (Array of String or XML.Element) • The current child strings and elements.
childrenCount (Number r/o) • Returns the current count of child strings and elements.
lastChild (String or XML.Element or null r/o) • Returns the last child of the element, or null if there are no children.
name (String r/o) • Returns the name of the element.
stringContents (String r/o) • Gathers all the immediate and descendent string children and returns them concatenated them as single string.
Instance Functions
Here are the functions that can be used on an instance of the XML.Element class:
childAtIndex(childIndex: Number) → (String or XML.Element or null) • Returns the child at the given index, or nil if the index is past the last child.
insertChild(child: String or XML.Element, childIndex: Number) → ( ) • Inserts the new child at the specified index. If the index is past the end of the current children, it is appended instead.
appendChild(child: String or XML.Element) → ( ) • Adds the new item to the end of the children.
removeChildAtIndex(childIndex: Number) → ( ) • Removes the child at the given index. If the index is past the end of the current children, no removal occurs.
removeAllChildren() → ( ) • Removes any existing children.
firstChildNamed(name: String) → (XML.Element or null) • Returns the first child element with the given name, or null if there is no such child.
firstChildAtPath(path: String) → (XML.Element or null) • Given a path which is a string separated by "/", returns the first element at that path.
firstChildWithAttribute(attribute: String, value: String) → (XML.Element or null) • Returns the first child with an attribute set to the given value.
attributeNamed(name: String) → (String or null) • Returns the value of the the given attribute or null if no value has been assigned.
setAttribute(name: String, value: String or null) → ( ) • Sets the value for the specified attribute. If the element already had a value for this attribute, it is replaced in place. If there previously was no value for this attribute, the attribute is appended to attributeNames. If the new value is null, the attribute is removed.
apply(function: Function(node: String or XML.Element) -> ApplyResult or null) → (ApplyResult or null) • Calls the supplied function for each child element or string in the receiver (including the receiver), passing that child as the single argument. The supplied function can optionally return a ApplyResult to skip enumeration of some elements.
Convert XML Text to XML Document Object
xmlText = `<recipe>
<title>Traditional Pound Cake</title>
<ingredients>
<ingredient>1 cup unsalted butter, softened</ingredient>
<ingredient>2 cups granulated sugar</ingredient>
<ingredient>4 large eggs</ingredient>
<ingredient>3 cups all-purpose flour</ingredient>
<ingredient>1 cup whole milk</ingredient>
<ingredient>1 tablespoon vanilla extract</ingredient>
<ingredient>1/2 teaspoon salt</ingredient>
</ingredients>
<steps>
<step number="1">Preheat your oven to 325°F (165°C). Grease and flour a 10-inch bundt pan or tube pan.</step>
<step number="2">In a large bowl, cream the butter and sugar together until light and fluffy.</step>
<step number="3">Add the eggs one at a time, beating well after each addition.</step>
<step number="4">Mix in the vanilla extract.</step>
<step number="5">In another bowl, combine the flour and salt. Gradually add to the creamed mixture alternately with the milk, beginning and ending with the flour mixture. Mix until just combined.</step>
<step number="6">Pour the batter into the prepared pan and smooth the top with a spatula.</step>
<step number="7">Bake in the preheated oven for 1 hour and 15 minutes, or until a toothpick inserted into the center of the cake comes out clean.</step>
<step number="8">Allow the cake to cool in the pan for 10 minutes before transferring to a wire rack to cool completely.</step>
</steps>
</recipe>`
behavior = new XML.WhitespaceBehavior(XML.WhitespaceBehavior.Type.Ignore)
data = Data.fromString(xmlText)
xmlDoc = XML.Document.fromData(data, behavior)
xmlDoc.rootElement.firstChildNamed("title").stringContents
//--> "Traditional Pound Cake"
Example: Read/Parse XML
The following examples use the fetch() method to retrieve textual weather data from NOAA (National Oceanic and Atmospheric Administration) in XML format that can be parsed for specific data.
In the final example, the current temperature at the San Francisco International Airport is retrieved and displayed in an alert. URLs for other NOAA weather stations can be found in an XML document posted by NOAA.
Retrieve XML
The first step in processing the XML weather data is to retrieve the data from the NOAA internet server using the fetch() function of the URL class:
Retrieve XML Data
url = URL.fromString('https://forecast.weather.gov/xml/
current_obs/ KSFO.xml') url.fetch(data => {
console.log(data.toString())
})
Converting the retrieved data to string results in XML textual data similar to the following.
NOTE: the opening and closing tags or the individual XML elements are highlighted in red, and any element attributes are colored green. The rootElement in the example data is named current_observation and contains all of the other informational elements.
Resulting XML Data
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet href="latest_ob.xsl" type="text/xsl"?>
<current_observation version="1.0"
xmlns:xsd="http://www.w3.org/
2001/ XMLSchema" xmlns:xsi="http://www.w3.org/
2001/ XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.weather.gov/
view/ >current_observation.xsd" <credit>NOAA's National Weather Service</credit>
<credit_URL>https://weather.gov/</credit_URL>
<image>
<url>https://weather.gov/images/
xml_logo.gif</url> <title>NOAA's National Weather Service</title>
<link>https://www.weather.gov</link>
</image>
<suggested_pickup>15 minutes after the hour</suggested_pickup>
<suggested_pickup_period>60</suggested_pickup_period>
<location>San Francisco, San Francisco International Airport, CA</location>
<station_id>KSFO</station_id>
<latitude>37.61961</latitude>
<longitude>-122.36558</longitude>
<observation_time>Last Updated on Feb 14 2022, 2:56 pm PST</observation_time>
<observation_time_rfc822>Mon, 14 Feb 2022 14:56:00 -0800</observation_time_rfc822>
<weather>Mostly Cloudy</weather>
<temperature_string>53.0 F (11.7 C)</temperature_string>
<temp_f>53.0</temp_f>
<temp_c>11.7</temp_c>
<relative_humidity>80</relative_humidity>
<wind_string>West at 18.4 MPH (16 KT)</wind_string>
<wind_dir>West</wind_dir>
<wind_degrees>290</wind_degrees>
<wind_mph>18.4</wind_mph>
<wind_kt>16</wind_kt>
<pressure_string>1018.7 mb</pressure_string>
<pressure_mb>1018.7</pressure_mb>
<pressure_in>30.08</pressure_in>
<dewpoint_string>46.9 F (8.3 C)</dewpoint_string>
<dewpoint_f>46.9</dewpoint_f>
<dewpoint_c>8.3</dewpoint_c>
<windchill_string>48 F (9 C)</windchill_string>
<windchill_f>48</windchill_f>
<windchill_c>9</windchill_c>
<visibility_mi>10.00</visibility_mi>
<icon_url_base>https://forecast.weather.gov/
images/ wtf/ small/</icon_url_base> <two_day_history_url>https://www.weather.gov/
data/ obhistory/ KSFO.html</two_day_history_url> <icon_url_name>bkn.png</icon_url_name>
<ob_url>https://www.weather.gov/
data/ METAR/ KSFO.1.txt</ob_url> <disclaimer_url>https://www.weather.gov/
disclaimer.html</disclaimer_url> <copyright_url>https://www.weather.gov/
disclaimer.html</copyright_url> <privacy_policy_url>https://www.weather.gov/notice.html</privacy_policy_url>
</current_observation>
Parsing XML: Element Names
In parsing the XML data, it is useful to first retrieve the names of the various XML elements, so you can a better idea of the what data is available to be retrieved.
This is accomplished by using the fromData(…) function of the XML.Document class to create an new XML.Document instance, and then calling the apply() function on the XML document’s rootElement to “walk” the document tree to retrieve the value of the name property of each found instance of the XML.Element class:
Parsing XML Data for Element Names
targetURL = URL.fromString(
'https://forecast.weather.gov/xml/current_obs/KSFO.xml'
)
targetURL.fetch(data => {
doc = XML.Document.fromData(data)
var elementNames = new Array()
doc.rootElement.apply(item => {
if (item instanceof XML.Element) {
elementNames.push(item.name)
}
})
console.log(JSON.stringify(elementNames))
})
The result of the previous script will be an array of the names of the various XML elements:
Names of the XML Elements
//--> ["current_observation",
"credit", "credit_URL", "image", "url", "title", "link", "suggested_pickup", "suggested_pickup_period", "location", "station_id", "latitude", "longitude", "observation_time", "observation_time_rfc822", "weather", "temperature_string", "temp_f", "temp_c", "relative_humidity", "wind_string", "wind_dir", "wind_degrees", "wind_mph", "wind_kt", "pressure_string", "pressure_mb", "pressure_in", "dewpoint_string", "dewpoint_f", "dewpoint_c", "windchill_string", "windchill_f", "windchill_c", "visibility_mi", "icon_url_base", "two_day_history_url", "icon_url_name", "ob_url", "disclaimer_url", "copyright_url", "privacy_policy_url"]
Retrieve, Parse, Display
Now that the names of the retrieved XML elements have been collected, you can choose the elements whose values you wish to retrieve and display to the user in an alert dialog. The value of an instance of the XML.Element class can be retrieved using the firstChildNamed(…) function and then getting the value of the element’s stringContents property:
Retrieve, Parse, and Display
url = URL.fromString(
'https://forecast.weather.gov/xml/current_obs/KSFO.xml'
)
url.fetch(data => {
doc = XML.Document.fromData(data)
loc = doc.rootElement.firstChildNamed("location").stringContents
keys = ["weather", "temperature_string", "wind_string"]
titles = ["Weather", "Temperature", "Wind"]
strings = keys.map((key, index) => {
value = doc.rootElement.firstChildNamed(key).stringContents
return `${titles[index]}: ${value}`
})
alertContent = strings.join("\n")
new Alert(loc, alertContent).show()
})
More Parsing Concepts
In the previous NOAA example, the individual locations of the NOAA XML data are listed as "station" elements (of the Station XML Index)(DOWNLOAD ZIP) with property keys and values:
A “station” Element
<station>
<station_id>KAKR</station_id>
<state>OH</state>
<station_name>Akron, Akron Fulton International Airport</station_name>
<latitude>41.0375</latitude>
<longitude>-81.46417</longitude>
<html_url>https://w1.weather.gov/
data/ obhistory/ KAKR.html</html_url> <rss_url>https://weather.gov/
xml/ current_obs/ KAKR.rss</rss_url> <xml_url>https://weather.gov/
xml/ current_obs/ KAKR.xml</xml_url> </station>
The value of the xml_url property is a link to the forecast data for the specified station:
Forecast Data Link
<xml_url>https://weather.gov/xml/current_obs/KLXN.xml</xml_url>
The “station” elements of the NOAA XML data can be identified by the value of their state property. For example, here's a script that retrieves the ID and NAME values for all of the Ohio (OH) locations from the source XML:
Retrieve Locations (stations) for Ohio (OH)
locations = new Array()
targetURL = URL.fromString('https://forecast.weather.gov/xml/
current_obs/ index.xml') targetURL.fetch(data => {
console.log(data.toString())
doc = XML.Document.fromData(data)
doc.rootElement.apply(item => {
if (item instanceof XML.Element && item.name === "station"){
if(item.firstChildNamed("state").stringContents === "OH"){
id = item.firstChildNamed("station_id").stringContents
name = item.firstChildNamed("station_name").stringContents
locations.push({"id":id,"name":name})
}
}
})
console.log(locations)
})
And the resulting pairs of Ohio locations:
Ohio Locations (ID and NAME)
[{"id":"KAKR",
"name":"Akron, Akron Fulton International Airport"}, {"id":"KAOH", "name":"Lima, Lima Allen County Airport"}, {"id":"KBJJ", "name":"Wooster, Wayne County Airport"}, {"id":"KBKL", "name":"Cleveland, Burke Lakefront Airport"}, {"id":"KCAK", "name":"Akron Canton Regional Airport"}, {"id":"KCGF", "name":"Cleveland / Cuyahoga"}, {"id":"KCLE", "name":"Cleveland Hopkins International Airport"}, {"id":"KCMH", "name":"John Glenn Columbus International Airport"}, {"id":"KDAY", "name":"Dayton, Cox Dayton International Airport"}, {"id":"KDFI", "name":"Defiance, Defiance Memorial Airport"}, {"id":"KFDY", "name":"Findlay, Findlay Airport"}, {"id":"KFFO", "name":"Dayton / Wright-Patterson Air Force Base"}, {"id":"KHAO", "name":"Butler County Regional Airport"}, {"id":"KHZY", "name":"Ashtabula - Northeast Ohio Regional Airport"}, {"id":"KILN", "name":"Wilmington, Airborne Airpark Airport"}, {"id":"KLCK", "name":"Rickenbacker Air National Guard Base"}, {"id":"KLHQ", "name":"Lancaster, Fairfield County Airport"}, {"id":"KLNN", "name":"Willoughby"}, {"id":"KLPR", "name":"Lorain / Elyria, Lorain County Regional Airport"}, {"id":"KLUK", "name":"Cincinnati, Cincinnati Municipal Airport Lunken Field"}, {"id":"KMFD", "name":"Mansfield - Mansfield Lahm Regional Airport"}, {"id":"KMGY", "name":"Dayton, Dayton-Wright Brothers Airport"}, {"id":"KMNN", "name":"Marion, Marion Municipal Airport"}, {"id":"KOSU", "name":"Columbus, Ohio State University Airport"}, {"id":"KPHD", "name":"New Philadelphia, Harry Clever Field"}, {"id":"KSGH", "name":"Springfield, Springfield-Beckley Municipal Airport"}, {"id":"KTDZ", "name":"Toledo - Toledo Executive Airport"}, {"id":"KTOL", "name":"Toledo - Toledo Express Airport"}, {"id":"KTZR", "name":"Columbus, Bolton Field Airport"}, {"id":"KUNI", "name":"OHIO U/ATHEN-ALBANY"}, {"id":"KVTA", "name":"Newark, Newark Heath Airport"}, {"id":"KYNG", "name":"Youngstown, Youngstown-Warren Regional Airport"}, {"id":"KZZV", "name":"Zanesville, Zanesville Municipal Airport"}, {"id":"KAXV", "name":"Neil Armstrong Airport"}, {"id":"KMWO", "name":"Hook Field Municipal Airport"}, {"id":"K4I3", "name":"Knox County Airfield"}, {"id":"KI68", "name":"Warren County Airport"}, {"id":"KPOV", "name":"Portage County Airport"}, {"id":"KDLZ", "name":"Delaware Municipal Airport"}, {"id":"KMRT", "name":"Marysville Union County Airport"}, {"id":"KVES", "name":"Versailles Darke County Airport"}, {"id":"KPCW", "name":"Port Clinton Carl R Keller Field Airport"}, {"id":"KI69", "name":"Batavia Clermont County Airport"}, {"id":"KUSE", "name":"Wauseon Fulton County Airport"}, {"id":"KRZT", "name":"Chillicothe Ross County Airport"}, {"id":"KI67", "name":"Cincinnati W Airport"}, {"id":"KUYF", "name":"London Madison County Airport"}, {"id":"KI19", "name":"Lewis A. Jackson Regional Airport"}, {"id":"KOWX", "name":"Ottawa Putnam County Airport"}, {"id":"KCDI", "name":"Cambridge Municipal Airport"}, {"id":"KI23", "name":"Fayette County Airport"}, {"id":"KPMH", "name":"Greater Portsmouth Regional Airport"}, {"id":"KOXD", "name":"Miami University Airport"}, {"id":"KI74", "name":"Urbana Grimes Field"}, {"id":"KI43", "name":"James A Rhodes Airport"}, {"id":"KVNW", "name":"Van Wert County Airport"}]
Writing From and To a Data Object
The following scripts demonstrate how to write to file from a JavaScript object, and how to convert the contents of an XML file onto a JavaScript object.
JavaScript Data Object to XML File
dataObj = {
"name":"Samantha",
"id":"com.apple.speech.synthesis.voice.samantha.premium",
"rate":"0.45"
}
config = new XML.Document.Configuration()
behavior = new XML.WhitespaceBehavior(XML.WhitespaceBehavior.Type.Ignore)
config.whitespaceBehavior = behavior
doc = new XML.Document("main-element", config)
keys = Object.keys(dataObj)
keys.forEach((key, index) => {
doc.addElement(key, function(){
doc.appendString(dataObj[key])
})
})
wrapper = FileWrapper.withContents("voice-data.xml", doc.xmlData())
folderURL = URL.documentsDirectory
fileURL = folderURL.appendingPathComponent("voice-data.xml")
wrapper.write(fileURL, [FileWrapper.WritingOptions.Atomic], null)
if(Device.current.type === DeviceType.mac){folderURL.open()}
XML to Data Object using Fetch()
folderURL = URL.documentsDirectory
fileURL = folderURL.appendingPathComponent("voice-data.xml")
dataObj = fileURL.fetch(function(data){
cdoc = XML.Document.fromData(data)
dataObj = new Object()
cdoc.rootElement.children.forEach(element => {
if(element.name){
dataObj[element.name] = element.stringContents
}
})
// LOG DATA ITEMS
keys = Object.keys(dataObj)
keys.forEach(key => {
console.log(key + ":", dataObj[key])
})
}, function(err){
new Alert(err.name, err.message).show()
})
XML to Data Object using Asynchronous Fetch()
(async () => {
try {
folderURL = URL.documentsDirectory
fileURL = folderURL.appendingPathComponent("voice-data.xml")
request = URL.FetchRequest.fromString(fileURL.string)
request.method = 'GET'
request.cache = "no-cache"
response = await request.fetch()
responseCode = response.statusCode
if (responseCode >= 200 && responseCode < 300){
console.log(response.bodyString)
xmlDoc = XML.Document.fromData(response.bodyData)
dataObj = new Object()
xmlDoc.rootElement.children.forEach(element => {
if(element.name){
dataObj[element.name] = element.stringContents
}
})
// LOG DATA ITEMS
keys = Object.keys(dataObj)
keys.forEach(key => {
console.log(key + ":", dataObj[key])
})
} else {
throw new Error(`The request responded with ${responseCode} error.`)
}
}
catch(err){
new Alert(err.name, err.message).show()
}
})();
Similar processes as functions:
Function: Store Data Object as XML
function storeDataAsXML(dataObj, xmlFileName){
try {
config = new XML.Document.Configuration()
behavior = new XML.WhitespaceBehavior(XML.WhitespaceBehavior.Type.Ignore)
config.whitespaceBehavior = behavior
doc = new XML.Document("main-element", config)
keys = Object.keys(dataObj)
keys.forEach((key, index) => {
doc.addElement(key, function(){
doc.appendString(dataObj[key])
})
})
wrapper = FileWrapper.withContents(xmlFileName, doc.xmlData())
folderURL = URL.documentsDirectory
fileURL = folderURL.appendingPathComponent(xmlFileName)
result = wrapper.write(fileURL, [FileWrapper.WritingOptions.Atomic], null)
return true
}
catch(err){
new Alert(err.name, err.message).show()
}
}
Function: Retrieve Stored XML Data
function retrieveStoredXMLData(xmlFileName){
folderURL = URL.documentsDirectory
fileURL = folderURL.appendingPathComponent(xmlFileName)
dataObj = fileURL.fetch(function(data){
cdoc = XML.Document.fromData(data)
dataObj = new Object()
cdoc.rootElement.children.forEach(element => {
if(element.name){
dataObj[element.name] = element.stringContents
}
})
return dataObj
}, function(err){
new Alert(err.name, err.message).show()
})
}
Parse XML RSS Feed
Here’s a script demonstrating how to parse an XML RSS feed for the Omni Automation Vids podcast to show the webpage for the latest espisode.
Webpage for Latest Episode of Omni Automation Vids
url = URL.fromString(
'https://omni-automation.com/rss/feed.xml'
)
url.fetch(data => {
doc = XML.Document.fromData(data)
channelElement = doc.rootElement.firstChildNamed("channel")
itemElements = channelElement.children.filter(item => {
if(item.name === "item"){return item}
})
lastItem = itemElements[itemElements.length - 1]
episodeWebpageURL = lastItem.firstChildNamed("link").stringContents
URL.fromString(episodeWebpageURL).open()
})