Home > Enterprise >  Retrieve XML values by attribute
Retrieve XML values by attribute

Time:09-30

public string[] UnpackXML(string xml_string)
{
    string response = xml_string;
    string[] return_values = new string[6];

    XmlDocument xml = new XmlDocument();
    xml.LoadXml(response);

    return_values[0] = xml.GetElementsByTagName("meas name=\"mt1\"")[0].InnerText.ToString();
    return_values[1] = xml.GetElementsByTagName("meas name=\"mt2\"")[0].InnerText.ToString();
    return_values[2] = xml.GetElementsByTagName("meas name=\"mt3\"")[0].InnerText.ToString();
    return_values[3] = xml.GetElementsByTagName("meas name=\"mt4\"")[0].InnerText.ToString();
    return_values[4] = xml.GetElementsByTagName("meas name=\"mt5\"")[0].InnerText.ToString();
    return_values[5] = xml.GetElementsByTagName("meas name=\"mt6\"")[0].InnerText.ToString();

    return return_values;
}

When running the above code, it fails with the given error code:

System.NullReferenceException: 'Object reference not set to an instance of an object.'

In the first line where I try to give return_values[0] a new value:

return_values[0] = xml.GetElementsByTagName("meas name=\"mt1\"")[0].InnerText.ToString();

The input to the UnpackXML is just an API response given as an XML string. The XML document has the following format:

<response location='location1'>
    <meas name='mt1'>14</meas>
    <meas name='mt2'>23</meas>
    <meas name='mt3'>65</meas>
    <meas name='mt4'>31</meas>
    <meas name='mt6'>32</meas>
</response>

Any ideas on how to fix this? I basically want to append specific fields from the XML file to an array. The XML attribute "name" exists with each line and has different values for the attribute. Is there a way to directly access the attribute values by giving the correct name = "nameValue", without looping through attributes and checking each specific value?

CodePudding user response:

The .GetElementsByTagName() is not suitable for your scenario since you are querying with XPath but not only with tag name.

Instead, you should use .SelectNodes() or .SelectSingleNode().

return_values[0] = xml.SelectNodes("//meas[@name=\"mt1\"]")[0]?.InnerText.ToString();
return_values[1] = xml.SelectNodes("//meas[@name=\"mt2\"]")[0]?.InnerText.ToString();
return_values[2] = xml.SelectNodes("//meas[@name=\"mt3\"]")[0]?.InnerText.ToString();
return_values[3] = xml.SelectNodes("//meas[@name=\"mt4\"]")[0]?.InnerText.ToString();
return_values[4] = xml.SelectNodes("//meas[@name=\"mt5\"]")[0]?.InnerText.ToString();
return_values[5] = xml.SelectNodes("//meas[@name=\"mt6\"]")[0]?.InnerText.ToString();

Or

xml.SelectSingleNode("//meas[@name=\"mt1\"]")?.InnerText.ToString();

Demo @ .NET Fiddle

Also work with the null-conditional operator ?. to handle the returned node is possible null to prevent Null Reference Exception (NRE).


For your further question for dynamic way instead of hard code (assume 6 names you need to write the statements for 6 times), you may work with loop and LINQ to get the node's InnerText by matching the attribute value as below:

string[] return_values = new string[6];
string[] names = new string[6] { "mt1", "mt2", "mt3", "mt4", "mt5", "mt6" };
XmlDocument xml = new XmlDocument();
xml.LoadXml(response);
        
for (int i = 0; i < return_values.Count(); i  )
{
    var node = xml.DocumentElement
        .ChildNodes
        .Cast<XmlNode>()
        .FirstOrDefault(x => x.Attributes["name"].Value == names[i]);
    return_values[i] = node?.InnerText.ToString();
}

Or

for (int i = 0; i < return_values.Count(); i  )
{
    return_values[i] = xml.SelectSingleNode($"//meas[@name=\"{names[i]}\"]")?.InnerText.ToString();
}

Demo @ .NET Fiddle

CodePudding user response:

In case you have multiple entries for the response, it's recommended to load the return_values dynamically instead of writing every possible entry manually:

 public string[] UnpackXML(string xml_string)
 {
     XmlDocument xml = new XmlDocument();
     xml.LoadXml(xml_string);

     var response_childrens = xml.SelectSingleNode("response").ChildNodes;

     string[] return_values = new string[response_childrens.Count];

     for (int i = 0; i < response_childrens.Count; i  )
     {
        return_values[i] = response_childrens[i].InnerText;
     }

      return return_values;
  }
  • Related