Parsing XML settings file using XmlReader in 2025.0.2

I have written a C++ component for AXC F 2152 that uses Arp::System::Commons::Xml::XmlReader to parse a settings file on startup.
Originally the component was written with SDK 2022.6. I now modified the component to use both old and new API. Using SKD 2022.6 it still works as expected, but when I compile the project using SDK 2025.0.2 and execute it on the plc, I get a runtime-exception as soon as I call xml_reader.ReadToNextSibling("Variable") :

Arp.Plc.Domain.Internal.PlcOperation.OperationResultReport   ERROR - Exception of type 'Arp::System::Commons::XmlException' was thrown
xml element found when expecting an end element first
The relevant section of the parsed xml file:
...
<Variables>
  <Variable name="..." uid="..." precision="." />
  <Variable name="..." uid="..." precision="." />
</Variables>
...

I read to the first Variable node using xml_reader.ReadToDescendant("Variable"), read its attributes, and then want to proceed to the next Variable node (if present). Thats when the exception occurs. When only one Variable node is present ReadToNextSibling(...) returns false, as expected.
I have a xsd-schema for the settings file and it checks out as a valid file, so I think the parsed file is not the problem. To me it seems like a bug in the 2025.0.2 firmware/SDK

Are there any updates? Can somebody reproduce this error?

I can’t reproduce this behaviour.
Here are the relevant parts of the code I’m using:

#include "Arp/System/Commons/Xml/XmlReader.hpp"
using namespace Arp::System::Commons::Xml;
    XmlReader myXmlReader1 = XmlReader::CreateForString("<Variables><Variable name="..." uid="..." precision="." /></Variables>"); 
    XmlReader myXmlReader2 = XmlReader::CreateForString("<Variables><Variable name="..." uid="..." precision="." /><Variable name="..." uid="..." precision="." /></Variables>"); 
    log.Info("Reading XML file 1 to 'Variables': {0}", myXmlReader1.ReadToFollowing("Variables"));
    log.Info("Reading XML file 1 to descendant 'Variable': {0}", myXmlReader1.ReadToDescendant("Variable"));
    log.Info("Reading XML file 1 to next sibling 'Variable': {0}", myXmlReader1.ReadToNextSibling("Variable"));
    log.Info("Reading XML file 2 to 'Variables': {0}", myXmlReader2.ReadToFollowing("Variables"));
    log.Info("Reading XML file 2 to descendant 'Variable': {0}", myXmlReader2.ReadToDescendant("Variable"));
    log.Info("Reading XML file 2 to next sibling 'Variable': {0}", myXmlReader2.ReadToNextSibling("Variable"));
 Code is built using the SDK for AXC F 2152 FW 2025.0.2 LTS and run on an AXC F 2152 FW 2025.0.3 LTS
 Result:
INFO  - Reading XML file 1 to 'Variables': true
INFO  - Reading XML file 1 to descendant 'Variable': true
INFO  - Reading XML file 1 to next sibling 'Variable': false
INFO  - Reading XML file 2 to 'Variables': true
INFO  - Reading XML file 2 to descendant 'Variable': true
INFO  - Reading XML file 2 to next sibling 'Variable': true

This is the expected result. There is no exception thrown. It would be useful to have a minimum code example that demonstrates the unexpected behaviour. If you can send this to the technical support team in your local Phoenix Contact office, they will be able to work on this with you.

It seems to be whitespace related, I have whittled it down to this:

  // // doesnt work
  // auto xml = "<Variables>"
  //            "    <Variable name="..." uid="..." precision="."/>"
  //            "    <Variable name="..."  uid="..." precision="."/>"
  //            "</Variables>";
  // // doesnt work
  // auto xml = "<Variables>
"
  //            "<Variable name="..." uid="..." precision="."/>
"
  //            "<Variable name="..."  uid="..." precision="."/>
"
  //            "</Variables>";
  // works
  auto xml = "<Variables>"
             "<Variable name="..." uid="..." precision="."/>"
             "<Variable name="..."  uid="..." precision="."/>"
             "</Variables>";
  auto xml_reader = XmlReader::CreateForString(xml);
  // parse variables
  if (!xml_reader.ReadToFollowing("Variables")) {
    log.Error("Invalid configuration file: Node "Variables" not found");
  }
  if (!xml_reader.ReadToDescendant("Variable")) {
    log.Error("Invalid configuration file: Node "Variable" not found");
  }
  do {
    log.Info("Read variable");
  } while (xml_reader.ReadToNextSibling("Variable"));

The XmlReader class basically wraps the functions provided by the open-source libxml/xmlreader.c. So if certain XML won’t work with libxml for any reason, then it also probably won’t work with the XmlReader class.
I’ve been trying to run the same XML through the xmlreader functions that correspond to the XmlReader methods that you’re using. Here is part of the (very rough) code that I’ve been using:
code.txtNote that while libxml2.so is included with the PLCnext Control firmware, the library is not included in the SDK. But it’s possible to check the behaviour of libxml in a custom C/C++ application on any platform, including Windows or Ubuntu.
Here are two of the strings I tried with the attached code:
1.

const String& buffer = "<Variables>"
      "<Variable name="..." uid="..." precision="."/>"
      "<Variable name="..." uid="..." precision="."/>"
      "</Variables>";
Result:
Created XML Reader for memory.
Reading the first element.
Local name of first element: Variables
Reading the second element.
Local name of second element: Variable
Reading the third element.
Local name of third element: Variable
2.
const String& buffer = "<Variables>"
      "  <Variable name="..." uid="..." precision="."/>"
      "  <Variable name="..." uid="..." precision="."/>"
      "</Variables>";
Result:
Created XML Reader for memory.
Reading the first element.
Local name of first element: Variables
Reading the second element.
Local name of second element: #text
Reading the third element.
Local name of third element: Variable

I don’t know why the whitespace makes libxml interpret the second element as being #text, but I think this explains why XmlReader doesn’t work in this case. This appears to be the way that libxml behaves - maybe this is expected based on the xml specifications?

code.txt

Another Phoenix Contact employee suggested using the (Try)ReadStartElement and (Try)ReadEndElement functions of the XmlReader class, as they are used by the firmware to parse all xml files and are thus more or less guaranteed to work properly.
I changed my code to use these functions instead and now it is able to parse the original xml file with its whitespace.