XML datatype in the ACF language
We have created the XML implementation as close as possible to the JSON implementation to make the learning curve as short as possible. However, there are some conceptual differences between the XML format and the JSON format.
Namespaces and Attributes: XML's use of namespaces and attributes offers a level of detail and organization not present in JSON. These features allow for more nuanced data structuring and meaning, especially in complex or hierarchical data models.
Array Representation: XML represents arrays differently than JSON. In XML, arrays are often implied through repeated child elements, whereas JSON uses explicit array notation. This difference affects how we'll parse and generate arrays in XML compared to JSON.
Addressing these conceptual differences is crucial in the design of the XML handling system that is both intuitive for users familiar with JSON in ACF and accurate to XML's structure and capabilities. The approach to namespaces, attributes, and array handling will significantly influence the syntax and functionality of the XML implementation.
Datatypes
We introduce two new datatypes for XML handling. The first is the XML document itself, declared exactly as JSON or any other datatype in ACF. The other is XML-references which is really a pointer pointing to a position in the XML structure.
Example:
XML myXMLDoc;
// Some initialization of the XML
myXMLDoc["rootNode.companies.company[]"] = XML("Name", "CompanyA", "Address", "....");
This means that in the XML structure, you have:
<?xml version="1.0" encoding="UTF-8"?>
<rootNode>
<companies>
<company>
<Name>CompanyA</Name>
<Address>....</Address>
</company>
</companies>
</rootNode>
XML references
To make a short-hand to work with a part of the XML structure, we can create a pointer that points to the first company record in the structure, and now you can add more fields to this:
XMLref node = myXMLDoc["rootNode.companies.company[1]"];
node["Phone"] = "12345678";
This changes the XML to look like this:
<?xml version="1.0" encoding="UTF-8"?>
<rootNode>
<companies>
<company>
<Name>CompanyA</Name>
<Address>....</Address>
<Phone>12345678</Phone>
</company>
</companies>
</rootNode>
XMLref as parameters and return-value in ACF functions
An XMLref variable is just a pointer to another XML variable's structure, so it cannot work without it. Once you return from a function, the XML variable that owns the data XMLref points to goes out of scope. Therefore, the compiler will issue a compilation error if you try to return an XMLref using the return statement.
However, you can have an XMLref as a parameter to another function. Operations on it will act on the calling function's XML variable that it points to.
ARRAYs in XML
XML doesn't have an array construct like JSON has. However, arrays are created using repetitive XML constructs instead. Like this example:
<rootNode>
<child>
<Elem1>Value1</Elem1>
<Elem2>Value2</Elem2>
</child>
<child>
<Elem1>Value3</Elem1>
<Elem2>Value4</Elem2>
</child>
</rootNode>
In ACF, this construct is made like this:
XML myXML;
myXML["rootNode.child[]"] = XML("Elem1", "Value1", "Elem2", "Value2");
myXML["rootNode.child[]"] = XML("Elem1", "Value3", "Elem2", "Value4");
This syntax tells the function to add a new child to the rootNode, containing the key/value pairs as described. This syntax looks similar to the JSON functions in ACF, but the difference is that the child node in JSON is not repeated, but the key/value pairs are added to a JSON array below child.
To get the value of the Elem2 tag for the second child in the example above, we would write:
string myValue = myXML["rootNode.child[2].Elem2"];
Counting size of ARRAY in XML
To get the number of repeated child elements in the above example, you can use the SizeOf function.
int NoOfChilds = SizeOf(myXML["rootNode.child"]);
List the keys in the XML
If you want to know what keys are present in an XML node, you can use the list_keys function, similar to the same for JSON.
// From the XML made under the heading "ARRAYs in XML"
string myKeys = list_keys(myXML["rootNode.child[1]"]);
This will give the following value in myKeys:
Elem1
Elem2
Attributes and namespaces
Attributes and namespaces is a feature that does not exist for JSON type, so here we have some syntax and functions that do not operate on JSON, only for XML.
Namespaces
Namespaces in XML are used to avoid element name conflicts in XML documents. They're similar to the concept of namespaces in programming languages. When multiple XML-based languages are combined in a single XML document, namespaces ensure that element names from one language don't conflict with those in another.
A namespace is defined using the xmlns attribute in the start tag of an element. The value of this attribute is a URI (not necessarily a working URL) that uniquely identifies the namespace. After a namespace is declared, elements or attributes can be associated with it using a prefix.
For example:
<root xmlns:ns="https://www.example.com/ns">
<ns:element>Content</ns:element>
</root>
Here, ns is the prefix associated with the namespace https://www.example.com/ns, and element belongs to this namespace. Namespace declarations apply to the element where they're declared and to all elements within that scope.
Attributes
XML attributes provide additional information about XML elements. They are always specified in the start tag of an element and generally come in name/value pairs like name="value". Attributes are used to provide properties of elements, such as configuration settings, identifiers, or other data that defines or describes the element.
Here's an example:
<book title="The Great Gatsby" author="F. Scott Fitzgerald" />
In this example, the book element has two attributes: title with the value "The Great Gatsby" and author with the value "F. Scott Fitzgerald". Attributes are a powerful way to add information to elements without cluttering the document's structure.
Using attributes and namespaces in the ACF language
As we can see, namespace definitions are just an attribute to an XML tag. To define attributes and namespace definitions, we use the XML_set_attribute function. The parameter is an XML variable with the optional path in it, then the name of the attribute, and then as a third parameter, the value of the attribute.
To make the XML above, we can write:
XML myXML;
myXML["book"] = "";
XML_set_attribute(myXML["book"], "title", "The Great Gatsby");
XML_set_attribute(myXML["book"], "author", "F. Scott Fitzgerald");
To set a namespace on the root node of an XML document:
XML myXML;
myXML["root.ns:element"] = "Content";
XML_set_attribute(myXML, "xmlns:ns", "https://www.example.com/ns");
Getting a list of attributes
Importing and exporting XML
To initialize an XML variable with content from a file or from a WEB service, you use a simple assignment.
Example:
XML myXMLdoc;
int x;
x = open("/path/to/xmlfile.xml", "r");
myXMLdoc = read(x);
close(x);
For exporting XML, you use the string conversion function:
XML myXMLdoc;
// creating some content in it.....
int x;
x = open("/path/to/xmlfile.xml", "w");
write(x, string(myXMLdoc));
close(x);
Paths can be applied to the XML variable to export only a part of the XML or import a document into a specific path.
Extracting values from XMLs where namespaces are used.
Sometimes when reading XML documents, you don't know the namespace prefixes of a given tag. We have a somewhat simplified approach when addressing paths inside XML. You can safely leave out the namespace prefix and use only the tag-name part of the tag.
For the example:
XML myXML;
myXML["root.ns:element"] = "Content";
XML_set_attribute(myXML, "xmlns:ns", "https://www.example.com/ns");
string theContent = myXML["root.element"];
// or
string theContent = myXML["root.ns:element"];
Both work fine to pull the content from the XML. If you have several element tags in different namespaces, you must obviously use the prefix to get the correct element.
