| Earth Observation Mission CFI Software Usage Guide for Object Oriented Software |

# READING XML FILES

The Earth Observation CFI provides a class called XmlFile (FileHandling library) that is dedicated to handle XML files.

## The interface to an XML file represented as a tree

Although the XmlFile class hides all these details to the programmer, a few basic concepts need to be presented here to understand how to use the library functions in order to achieve a given goal. We assume that the user has some basic knowledge of XML and understands the concepts of XML elements, attributes and text nodes:

- An element B which is contained directly within an element A is referred to as child of A. Unsurprisingly, A is said to be the parent of B.
- Two elements, B and C, that are children of a common parent A are said to be sibling elements.

Any well-formed XML file may be viewed as a hierarchy of elements, attributes and values that can be represented as a tree. In fact this is the in-memory representation used by libxml when an XML file is read into memory.

## Reading a file

In order to read an XML file, the following sequence should be followed:

- Create XmlFile object by calling the XmlFile constructors.
  During this operation the file to read is loaded into memory.
  There are two constructors to create the object:
  1. Create the object with the input file name:
     `XmlFile myXml(filename)`
  2. Create the object with the empty constructor. In this case, the file has to be loaded
     into memory afterwards calling the read method with the input file name:
     `XmlFile myXml()
     myXml.read(filename)`
  Note that the FileHandling library may handle in parallel up to XFCFI_MAX_FILES_NUMBER files.
- Read the file using the XmlFile methods for reading.
  There are two ways of reading a file:
  1. Sequential reading: the file is read in order, visiting all its nodes.
  2. Random reading: it is possible to navigate throug the XML tree to read specific elements or atrributes
  These concepts are developed in more detail below.
- Clean memory: The in-memory representation of an XML file normally takes a significant amount of memory space, typically between 7 and 10 times the size of the file on disk. For this reason it is important to provide the means for releasing the memory allocated after being used. This can be done using the `XmlFile::clean`, or it is done when the object XmlFile is destroyed.

## Sequential Reading

The sequential reading allows to read a file ensuring that all elements within the XML tree are visited only once using recursively the following rules:

- If an element has children elements that have not been visited, the next element to visit is its first child.
- If all element children have been visited or the element has no children, the next element to visit is its next sibling.
- If an element has no next sibling, then go to the parent and apply the second rule.

The XmlFile class uses an iterarator that points to the last read element. This iterator points to the root element when the file is loaded in memory (after the calling the constructor or the `read` method)
Starting with that iterator, all elements are visited once and only once until the iterator returns to the root element. The iterator behaves as follows:

- If the requested element is found, the iterator is set to point to the found element.
- When an `XmlFile::getElementValueAs*` method is called several times, the search will start just after the point where the iterator was left in the preceding call.
- In case the requested element name is not found, the iterator will not be moved
- If the element is found and an error occurs, i.e., because of a type conversion error or an empty element, the iterator will point to the found element.
- The `XmlFile::root()` method allows to reset the iterator to the root element in order to start over from the beginning.

Following there is a summary of the methods for reading the files sequentially:

- `XmlFile::getElementValueAs*` (* = Int, Real, String):
  These methods will return the value of the first element found whose name matches exactly the element name provided as input.
  For instance:
  `anx = myXml.getElementValueAsReal("ANX_Longitude")`

- `XmlFile::getElementArrayAs*`(* = Int, Real, String):
  These methods read all elements with a given name. A call to any of these functions will first set the iterator pointing to the root element, then will traverse the file looking for the requested elements and will return the vector of values leaving the iterator pointing to the last element read.
  Example:
  `intArray = myXml.getElementValueArrayAsInt("Absolute_Orbit", intArray)`

- `XmlFile::getAttributeValueAs*`(* = Int, Real, String):
  These methods read the attribute corresponding to the input attribute name and the element name that contains it. If the iterator is pointing to an element name different from the specified one, this element is first looked for, the iterator is moved to it and then the requested attribute is looked for within that element. If the iterator is already pointing to an element whose name matches the specified one, the attribute is directly searched for within that element.
  Example:
  `numOrbitChanges = myXml.getElementValueArrayAsInt("List_Of_Orbit_Changes", "count")`

See Sequential reading example

## Random reading

Random access to the in-memory representation of an XML file is achieved using XPath expressions. XPath expressions allow addressing XML elements and attributes using a syntax that resembles the one used for addressing files and directories in a hierarchical file system and is based on the following rules:

- Element names are separated by the "/" character.
- XPath expressions may be absolute (i.e., starting with the "/" character and listing all elements from the root to the requested one), or relative to the current position of the iterator (i.e., without the leading "/").
- "." is used for the current element, and ".." for the parent element as it is used for directories.
- If an element has a collection of children with the same name, individual elements may be accessed using their element name followed by an integer index between square brackets, being 1 the index to the first element (i.e., A[1], for the first occurrence of element A, A[2] for the second and so on)
- Attribute names are preceded by the "@" character.

Following there is a summary of the methods for reading the file with the path expresion:

- The `XmlFile::getPathValueAs*` (* = Int, Real, String):
  Methods for reading the element value specified by the input XPath expression.
  Example:
  `mlstDrift = myXml.getPathValueAsReal ("/Earth_Explorer_File/Data_Block/List_of_Orbit_Changes/Orbit_Change[1]/Cycle/MLST_Drift")`
- The `XmlFile::getPathArrayAs*` (* = Int, Real, String):
  Methods for reading a set of element values specified by the input XPath expression.
  Example:
  `mlstDriftArray = myXml.getPathArrayAsReal ("/Earth_Explorer_File/Data_Block/List_of_Orbit_Changes/Orbit_Change[*]/Cycle/MLST_Drift")`

On these methods iterator has the same behaviour as in the `XmlFile::getElementValueAs*` familliy of methods.

See Random reading example

## Random access

Together with these functions for reading, the class provides methods for a random access to a requested tag:

- `XmlFile::root`: Go to the root element
- `XmlFile::next`: Go to the next element:
- `XmlFile::nextSibling`: Go to the next sibling
- `XmlFile::toElement`: Go to the element next element with the requested name
- `XmlFile::toPath`: Go to the element element with the requested path
- `XmlFile::currentElementName`: Get the current element name
- `XmlFile::currentPathName`: Get the current path name
- `XmlFile::findValueInElement`: Find the Xpath with a given element name and value
- `XmlFile::findValueInPath`: Find the Xpath with a given element Xpath and value. The input XPath expression must contain the `[*]` character sequence in order to indicate the elements in a sequence (list) that are iterated.

All these functions set the iterator pointing to the found element. If the search operation returns an error, the iterator is not moved.

See Random reading example

## Examples

#### Sequential reading example

    // ------------------------------------------------------------------------
    //
    //                           C++ Example Program.
    //                           READING XML FILES: Sequential reading
    //
    // ------------------------------------------------------------------------

    // Non-EOCFI include files
    #include <string>
    #include <vector>
    #include <iostream>

    // EOCFI includes
    #include "XmlFile.h"
    #include "FixedHeader.h"
    #include "FileHandlingData.h"

    // Namespaces
    using namespace EECFI;
    using namespace std;

    // Main program
    int main (int argc, char *argv[])
    {
      //-------------------------------------------------
      //-------------------------------------------------

      try
      {
        long i;

        // Load XML file into memory
        XmlFile myXml("./osf.xml");

        //Reading element "File_Description" (First element with that tag name)
        string stringValue = myXml.getElementValueAsString("File_Description");
        cout << "File_Description: " << stringValue << endl;

        // Reading an attribute: Number of elements in the list
        long orbitNumber;
        orbitNumber = myXml.getAttributeValueAsInt("List_of_Orbit_Changes", "count");
        cout << "List_Of_Orbit_Changes contain " << orbitNumber << " Elements" << endl;

        // Reading the list of all absolute orbits in the list
        vector <long> integerArray;
        integerArray = myXml.getElementArrayAsInt("Absolute_Orbit", integerArray);
        for (i = 0 ; i < integerArray.size() ; ++i)
        {
          cout << "Orbit Number " << i <<" = " << integerArray[i] << endl;
        }

        // Free memory (optional: the class destructor would free the memory in anycase)
        myXml.clean();

      }//end try
      catch (CfiError cfiError)
      {
        //If an exception is thrown, there have been errors during execution
        vector<string> errorMessages;

        // Get error messages
        cfiError.getMsg(errorMessages);

        // Pring Error messages
        for (long i = 0; i < errorMessages.size(); i++)
        {
          cout << errorMessages[i] << endl;
        }
      }

      return 0;

    }

#### Random reading example

    // ------------------------------------------------------------------------
    //
    //                           C++ Example Program.
    //                           READING XML FILES: Random reading
    //
    // ------------------------------------------------------------------------

    // Non-EOCFI include files
    #include <string>
    #include <vector>
    #include <iostream>

    // EOCFI includes
    #include "XmlFile.h"
    #include "FixedHeader.h"
    #include "FileHandlingData.h"

    // Namespaces
    using namespace EECFI;
    using namespace std;

    // Main program
    int main (int argc, char *argv[])
    {
      //-------------------------------------------------
      // Put all the code inside try-catch statements
      // so, if something fails, an exception is thrown
      //-------------------------------------------------

      try
      {
        long i;
        XmlFile myXml;

        // Load XML file into memory
        myXml.read("./osf.xml");

        //Reading element "File_Description" (read an specific XPath)
        string stringValue = myXml.getPathValueAsString("/Earth_Explorer_File/Earth_Explorer_Header/Fixed_Header/File_Description");
        cout << "File_Description: " << stringValue << endl;

        //Reading element "ANX_Longitude" from the 3rd element in the list (read an specific XPath)
        double doubleValue = myXml.getPathValueAsReal("/Earth_Explorer_File/Data_Block/List_of_Orbit_Changes/Orbit_Change[3]/Cycle/ANX_Longitude");
        cout << "ANX_Longitude(3): " << doubleValue << endl;

        // Reading the list of all absolute orbits in the list
        vector <long> integerArray;
        integerArray = myXml.getPathArrayAsInt("/Earth_Explorer_File/Data_Block/List_of_Orbit_Changes/Orbit_Change[*]/Orbit/Absolute_Orbit", integerArray);
        for (i = 0 ; i < integerArray.size() ; ++i)
        {
          cout << "Orbit Number " << i <<" = " << integerArray[i] << endl;
        }

        // Search an element with a given XPath
        string searchPath;
        string foundPath;

        searchPath = "/Earth_Explorer_File/Data_Block/List_of_Orbit_Changes/Orbit_Change[*]/Orbit/Absolute_Orbit";

        foundPath = myXml.findValueInPath(searchPath, "7488");

        cout << "Orbit Value found in XPath = " << foundPath << endl;

        // Free memory (optional: the class destructor would free the memory in anycase)
        myXml.clean();

      }//end try
      catch (CfiError cfiError)
      {
        //If an exception is thrown, there have been errors during execution
        vector<string> errorMessages;

        // Get error messages
        cfiError.getMsg(errorMessages);

        // Pring Error messages
        for (long i = 0; i < errorMessages.size(); i++)
        {
          cout << errorMessages[i] << endl;
        }
      }

      return 0;

    }