Attempting to read and write emProject files as XML

Segger Embedded Studio project files appear to be XML formatted with a custom DOCTYPE.

<!DOCTYPE CrossStudio_Project_File>
<solution Name="ble_app_MA_lightbulb_pca10040_s132" target="8" version="2">
  <project Name="ble_app_MA_lightbulb_pca10040_s132">
    <configuration
      Name="Common"
      arm_architecture="v7EM"
      arm_core_type="Cortex-M4"
      arm_endian="Little"
      arm_fp_abi="Hard"
      arm_fpu_type="FPv4-SP-D16"
      arm_linker_heap_size="8192"
      arm_linker_process_stack_size="0"
      arm_linker_stack_size="8192"
      arm_linker_treat_warnings_as_errors="No"
   ...
   />
   <configuration
    Name="Debug"
    c_preprocessor_definitions="DEBUG; DEBUG_NRF"
    gcc_optimization_level="None" />
</solution>

Reading

In VB.NET,

Private xmlDoc As XmlDocument = New XmlDocument()
xmlDoc.PreserveWhitespace = True
xmlDoc.XmlResolver = Nothing
xmlDoc.Load(filename)

Once the XML is loaded, an attribute such as c_user_include_directories can be read using this syntax

 sIncludes = xmlDoc.SelectSingleNode("solution/project/configuration[@c_user_include_directories]").Attributes("c_user_include_directories").Value

Writing

However, writing out an this XML document object back to a file has a few problems. The DOCTYPE tag changes in that additional square brackets [] appear at the end due to a bug in the .NET framework, so you get <!DOCTYPE CrossStudio_Project_File []> which although is valid XML, causes SES to say that the file is not a valid project. 

The workaround is to create a new XML Document object and replace the Document Type with a new one, with Nothing (in VB.NET, equivalent to null in C#) for the subset object, as distinct from an empty string.

Dim n As XmlDocument = New XmlDocument()
n = xmlDoc.Clone()

Dim parent As XmlNode = n.DocumentType.ParentNode
'4th param in CreateDocumentType has to be Nothing to avoid getting [] in DOCTYPE, which is what you get by default and with empty string
            
parent.ReplaceChild(n.CreateDocumentType("CrossStudio_Project_File", Nothing, Nothing, Nothing), n.DocumentType)

n.Save(w)

Using the replaced DOCTYPE leaves a space after the CrossStudio_Project_File but SES copes with this, and is a minor cosmetic effect. But a worse problem is that the original project file uses a mixture of attribute each on their own line and in-line attributes, which the .NET Framework forces to consistency. For example, the configuration attributes Name, arm_architecture etc are on their own lines:

<configuration
      Name="Common"
      arm_architecture="v7EM"
      arm_core_type="Cortex-M4"
      arm_endian="Little"

but the files in folders are in-line

<folder Name="nRF_Log">
      <file file_name="../../../../../../components/libraries/experimental_log/src/nrf_log_backend_rtt.c" />
      <file file_name="../../../../../../components/libraries/experimental_log/src/nrf_log_backend_serial.c" />
      <file file_name="../../../../../../components/libraries/experimental_log/src/nrf_log_backend_uart.c" />
      <file file_name="../../../../../../components/libraries/experimental_log/src/nrf_log_default_backends.c" />
      <file file_name="../../../../../../components/libraries/experimental_log/src/nrf_log_frontend.c" />
      <file file_name="../../../../../../components/libraries/experimental_log/src/nrf_log_str_formatter.c" />
    </folder>

If the in-line attributes are written back on their own lines, this does not affect SES ability to read the project in correctly, but still leads to a problem.

Attribute line formatting problem

Although there are PreserveWhitespace, Indent and NewLineOnAttributes properties of XMLDocument, no combination of these can reproduce the inconsistent formatting of the original project file. My use case was to change only a specific line, leaving the rest of the project file unchanged in a way that diff tools (WinMerge) will only see a change to that line (the c_user_include_directories attribute).

Therefore it may be easier to treat the emProject file as simply lines of text rather than XML if we wish to write back a file which should be as unchanged as possible for those lines which were not intentionally changed.

Lesson: Sometimes the right thing to do is treat XML files as just lines in a file instead of using the built in .NET XML handlers which can overcomplicate things.