Description

This class provides access to an XML tree.  Often, XML trees within the SC plugin API will have been obtained from the job database and will contain Job XML elements, however any valid XML can be represented.  This class does not provide methods for reading or writing XML files, or parsing XML fragments.  For this functionality, see tsc_XmlReader and tsc_XmlWriter.

A tsc_XmlElement object, and the tree it belongs to, stand separate from any object it may be related to or been created from.  If the XML is modified, only the element in memory is changed and there is no effect on any other object or the Job XML.

Each tsc_XmlElement has a set of properties, plus a reference to the next in a chain of siblings (other elements at the same level), and a reference to the first child element (other elements at the next level).  With these, the whole subtree is accessible.  The root element in a tree is also a tsc_XmlElement object which has no siblings, only child elements.  It is not possible to add siblings to the root.

An indexing system makes it unnecessary to traverse the tree to find a particular item.  Instead, navigation to any element within the tree is possible using only the subscript (square brackets - [ ]) operator.

If any call that would return a tsc_XmlElement fails, the "non-existent" element is returned; this element is fully functional although its navigation methods always return itself.  Other properties return appropriate null values, such as an empty string for the name or value.  This allows any sequence of calls or operations to be strung together, and only a single Exists test needs to be done at the end.  Thus the construct:

someXml["PointRecord"]["Grid"]["North"].Exists()

would return false if any of the elements - PointRecord, Grid, or North - was not found.

XML Namespaces

Basic support for XML namespaces was added in version 2023.10.

Namespaces are a way to avoid name clashes when multiple document types are present in one XML tree. A description can be found at https://en.wikipedia.org/wiki/XML_namespace. There are some added methods, and namespace prefixes are now permitted in a number of other methods that take the element name, or a path through element names, as a parameter. These are described below.

Methods to access the XML data

bool Exists () const;
Tests if the element exists.  If a function which returns a tsc_XmlElement fails (such as a subscript ([]) operator), it will instead return the "non-existent" element which returns false from the Exists function.  All successful results return an element that exists.

tsc_String Name () const;
This function returns the name of the element.

tsc_XmlElement Clone() const;
Creates a deep copy of the XML element and it s children.

tsc_XmlElement operator[] (const char* childElementName) const;
The subscript operator (string) locates a child element of the given name.  Names are case-sensitive, and the non-existent element is returned if the named child is not found.  Only immediate children may be located; this method does not recurse deeper. The C++ language allows subscripts to be concatenated, to search for any element by name within the xml subtree. 

For example, the following could be used to return the North coordinate of a point:

double north = theXml["PointRecord"]["Grid"]["North"].DoubleValue();

Also see the example at the bottom of this page.

tsc_XmlElement operator[] (int occurrence) const;
This subscript operator (integer) allows access to multiple identically-named child elements.  Passing a zero returns the same element, 1 returns the next identically named sibling, and so on.  If the element does not exist, the non-existent element is returned.

For example, the following statement would return the <Price> child element of the third ocurrence of <Item>, or 95.99 in the example xml below.

tsc_XmlElement thirdPrice = myXml["item"][2]["price"];

<shoes>   

  <item size=7>

    <price>93.41</price>

  </item>  

  <item size=8>

    <price>94.62</price>

  </item>

  <item size=9>

    <price>95.99</price>

  </item>

</shoes>

Likewise, the size attribute of the second item (8) could be returned by:

int secondSize = myXml["item"][1].IntAttrib("size");

Also see the example at the bottom of this page.

tsc_String PathValue (const char* path) const;
This method provides another way to access any node in the XML using a "path".  The path is a string containing a series of element names separated by slashes (/), possibly followed by an attribute name separated by an @.  The value is always returned as a string.  In the above example, the first shoe's size and price could be obtained by:

tsc_String size1 = myXml.PathValue("item@size");
tsc_String price1 = myXml.PathValue("item/price");

Note that the root element's name is not included in the path, and that only the first of a number of identical element names can be accessed.

tsc_String InnerXml() const;
Returns a string containing the formatted XML for the content of the element, excluding the element's own tag, and recursively including all child elements.  Each element is placed on a new line, but no indenting is performed.

tsc_String OuterXml() const;
Returns a string containing the formatted XML for the element and all its children.  Each element is placed on a new line, but no indenting is performed.

tsc_String StringValue() const;
Returns the value of the element as a string.  The string will be empty if the element contains only child elements.

bool BoolValue (bool* valid = NULL) const;
Returns the value of the element as a boolean.  The optional valid parameter is set to false if the string value is not "true" or "false" (case-insensitive), otherwise it is set to true.

[From version 1.70: Any whitespace (spaces, tabs, newlines, etc) which may be present before or after the value is ignored.]

int IntValue (bool* valid = NULL) const;
Returns the value of the element as a 32-bit integer.  The optional valid parameter is set to false if the value is not numeric or its value is out of range; otherwise it is set to true.

[From version 1.70: Any whitespace (spaces, tabs, newlines, etc) surrounding the value is ignored.]

double DoubleValue (bool* valid = NULL) const;
Returns the value of the element as a floating-point double.  The optional valid parameter is set to false if the value is not a valid real number or its value is out of range; otherwise it is set to true. 

[From version 1.70: Any whitespace (spaces, tabs, newlines, etc) which may be present before or after the value is ignored.]

bool HasAttrib (const char* attribName) const;
Returns true if this element has an attribute of the supplied name.

tsc_String StringAttrib (const char* attribName, bool* ok = NULL) const;
Returns the value of the named attribute as a string.  If the optional ok parameter is supplied, it is set to false if the named attribute is not present; otherwise it is set to true.  In any case, if the attribute name does not exist within the node, an empty string is returned.  

bool BoolAttrib (const char* attribName, bool* ok = NULL) const;
Returns the value of the named attribute as a boolean.  If the optional ok parameter is supplied, it is set to false if the named attribute is not present or does not equal 'true' or 'false' (case insensitive).  In any case, if the attribute name does not exist within the node, a false value is returned.

int IntAttrib (const char* attribName, bool* ok = NULL) const;
Returns the value of the named attribute as an integer.  If the ok parameter is supplied, it is set to false if the named attribute is not present or is not a valid 32-bit integer, or true otherwise.     In any case, if the attribute name does not exist within the node, a zero value returned.

int HexAttrib (const char* attribName, bool* ok = NULL) const;
Returns the value of the named attribute as an integer. The value is treated as a hexadecimal string and must contain only characters 0-9 and a-f.  A preceding "0x" is allowed but not necessary.  If the optional ok parameter is supplied, it is set to false if the named attribute is not present or is not a valid 32-bit hex integer, or true otherwise.   In any case, if the attribute name does not exist within the node, a zero value returned.

double DoubleAttrib (const char* attribName, bool* ok = NULL) const;
Returns the value of the named attribute as a double.  If the optional ok parameter is supplied, it is set to false if the named attribute is not present or is not  a valid double value, or true otherwise.   In any case, if the attribute name does not exist within the node, a double_Null value returned.

Output and formatting

Note: For greater control of the formatting process and to create standard XML files, use the tsc_XmlWriter class rather than the ToString function.

tsc_String ToString () const override;
Returns a string containing the element and its children.  Newline (\r\n) sequences are placed between elements, but otherwise no formatting of the string occurs.  This method is identical to OuterXml().  Any Xml special characters in element values or attributes will be converted to the equivalent Xml entity, such as < to &lt;

virtual tsc_String ToString (int initialIndent) const;
Returns a formatted string containing the element and its children.  Newline sequences are inserted between elements, and each element (that is, each line) is indented by 4 spaces for each level of nesting.  The entire output is further indented by 4 spaces per level given in the initialIndent parameter.  Xml special characters will be converted to the equivalent Xml entity, such as > to &gt;

Methods ​for modifying an element

Available only in SC versions 1.70 and higher.

These methods allow modification of the in-memory representation of an XML tree.  This does not affect anything other than the XML; there is no connection to any originating object such as a database record.

void SetName (const char* name);
Replaces the name of the element.  The name must start with an alphabetic character, and must not contain illegal characters such as '<', '>', '&', whitespace, or quote characters.

void SetValue (const char* value);
Replaces the value of the element (the string between <name> and </name>).  Child elements are not affected.  Escaping or conversion of special characters is not required; this will occur when an output method is called - InnerXml, OuterXml, ToString, or by using the tsc_XmlWriter class.

void ClearAttributes ();
Removes all attributes from the element.

void AddAttribute (const char* name, const char* value);
Adds an attribute to the end of the attribute list.  The validity of the name is checked and any special characters in the value are converted to XML entities.

void DeleteAttribute (const char* name);
Permanently removes the named attribute from the element.  Nothing happens if the named attribute does not exist.

void SetAttribute (const char* name, const char* value);
Modifies the value of an attribute if it exists or appends a new attribute if it is missing.

tsc_XmlElement AddChild (const char* name);
Adds a new child after the last of the element's children.  The new element is returned.

void DeleteChild (tsc_XmlElement& child);
Removes the child element and any children it may have.  The child element provided must be a child of the element on which the method is called.

tsc_XmlElement InsertAfter (const char* name);
Inserts a new sibling element after this element.  The new element is returned.

Creating a ​new XML tree

Available in SC version 1.70 and higher.

tsc_XmlElement (const char* rootElementName);
This constructor creates a new XML root element, within which a new tree may be formed by adding child elements.

Navigation methods for traversing the XML tree

tsc_XmlElement Sibling() const;
Returns the next sibling element.  That is, the next element at the same nesting level.  If there are no further siblings, the non-existent element is returned.

tsc_XmlElement Child() const;
Returns the first child of the element, or the non-existent element if no child exists.  To iterate through all the child elements, call Sibling() on each child element in turn.

bool HasChild() const;
Returns true if the element has any child element.  Equivalent to element.Child().Exists()

tsc_XmlElement Root() const;
Returns a reference to the root of the entire tree that contains this element.

Example of use

/*

This examples assumes the following elements exist in a point record XML fragment (simplified):

<root>

  <PointRecord>

    <Name>P101</Name>

    <Grid>

      <North>303475.23461</North>

      <East>818475.77691</East>

      <Elevation>186.55548</Elevation>

    </Grid>

    <Deleted>false</Deleted>

  </PointRecord>

</root>

*/


// Return the grid coordinate of the first point in the database.

// Note that this method is VERY inefficient.  In practice, use tsc_Point::Grid()

bool GetFirstPointGrid (double& north, double& east, double& elev)

{

    bool success = false;

    tsc_PointList points = tsc_Job::CurrentJob()->Database().Points().Snapshot();


    if (points.Count() > 0)

    {

        tsc_XmlElement xml = points[0].GetEntityJxl();

        bool ok;

        bool deleted = xml["Deleted"].BoolValue(&ok);


        if (ok && !deleted)

        {

            tsc_String pointXml = xml.OuterXml();

            tsc_String pointName = xml["PointRecord"]["Name"].StringValue();

            tsc_XmlElement grid = xml["PointRecord"]["Grid"];

            if (!grid.Exists()) grid = xml["PointRecord"]["ComputedGrid"];


            if (grid.Exists())

            {

                north = grid["North"].DoubleValue();

                east  = grid["East"].DoubleValue();

                elev  = grid["Elevation"].DoubleValue();

                success = true;

            }

        }

    }

    return success;

}


// This function creates and returns the point record XML very similar to that at the top of this example.

// Note use of tsc_Format::Scalar(). We do not want unit abbreviations in our xml file!

tsc_String CreatePointXml (tsc_String name, double north, double east, double elevation)

{

    tsc_XmlElement xxml ("root");

    tsc_XmlElement xpoint = xxml.AddChild ("PointRecord");

    xpoint.AddChild ("Name").SetValue (name);

    tsc_XmlElement xgrid = xpoint.AddChild("Grid");

    xgrid.AddChild ("North").SetValue (tsc_Format::Scalar(north));

    xgrid.AddChild ("East").SetValue (tsc_Format::Scalar(east));

    xgrid.AddChild ("Elevation").SetValue (tsc_Format::Scalar(elevation));

    xpoint.AddChild ("Deleted").SetValue("false");

    return xxml.ToString(0);

}