0

I have the following structure that I would like to read through in C#. It is used to store a matrix-like set of elements (square in the instance).

<Parent>
    <Child AttrName11="" AttrName12="" AttrName13="" />
    <Child AttrName21="" AttrName22="" AttrName23="" />
    <Child AttrName31="" AttrName32="" AttrName33="" />
</Parent>

I am searching for say a given diagonal attribute value based on an input attribute name, for example, AttrNameXX, and wish to go through the different rows until I get the first match on AttrNameXX, that is, within {AttrName11, AttrName22, AttrName22}. To do so I need to move from one Child to the next.

  • I have opened an

    XmlDocument doc
    
  • I have reached the parent node with

    doc.SelectSingleNode(ParentPath)
    
  • I have reached the first child node with

    doc.SelectSingleNode(ChildPath)
    

I cannot find the command to move to the next child node.

4 Answers4

0

The XML Attributes are not separated by commas. Remove the commas.

<Parent>
    <Child AttrName11="a" AttrName12="b" AttrName13="c" />
    <Child AttrName21="d" AttrName22="e" AttrName23="f" />
    <Child AttrName31="g" AttrName32="i" AttrName33="j" />
</Parent>

I would read the values into a 2d-array (matrix). It is easier to reason about it without having to worry about details of parsing XML at the same time.

In the following I am not using System.Xml.XmlDocument but System.Xml.Linq.XDocument. See: XDocument or XmlDocument (XDocument is the more modern one of the two).

string[,] matrix = new string[3, 3];
for (int i = 0; i < 3; i++) {
    XElement row = doc.Root.Elements().ElementAt(i);
    for (int j = 0; j < 3; j++) {
        XAttribute col = row.Attributes().ElementAt(j);
        matrix[i, j] = col.Value;
    }
}

If you have other attributes than the ones shown above, accessing the attributes by index yield the wrong result. But you can also access the attributes by name:

matrix[i, j] = row.Attribute($"AttrName{i + 1}{j + 1}")?.Value;

We can test that we have read the right data with:

for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        Console.Write(matrix[i, j] + " ");
    }
    Console.WriteLine();
}

We can search for a diagonal element like this:

// Searching for "e" diagonal element:
for (int i = 0; i < 3; i++) {
    if (matrix[i, i] == "e") {
        Console.WriteLine($"m[{i},{i}]= {matrix[i, i]}");
    }
}

If the number of rows and columns is variable, you can also loop the XML with foreach:

List<List<string>> varMatrix = new();
foreach (XElement row in doc.Root.Elements()) {
    var line = new List<string>();
    varMatrix.Add(line);
    foreach (XAttribute col in row.Attributes()) {
        line.Add(col.Value);
    }
}
Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
0

It's as simple as

XmlNode node = doc.SelectSingleNode(ChildPath);
var nextNode = node.NextSibling();

Given that you fix your xml, so it's valid of course.

You can keep calling NextSibling in a loop until it returns null.

Palle Due
  • 5,929
  • 4
  • 17
  • 32
0

Using Xml Linq. I used KeyValuePair<string,string> which the key is the attribute name and the value is the attribute value like [AttrName11,value]

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;


namespace ConsoleApplication2
{
    class Program
    {

        const string FILENAME = @"c:\temp\test.xml";

        static void Main(string[] args)
        {
            XDocument doc = XDocument.Load(FILENAME);
            XElement parent = doc.Descendants("Parent").FirstOrDefault();

            List<List<KeyValuePair<string, string>>> values = parent.Elements()
                .Select(x => x.Attributes().Select(y => new KeyValuePair<string, string>(y.Name.LocalName, (string)y)).ToList()).ToList();

        }
    }
}
jdweng
  • 33,250
  • 2
  • 15
  • 20
0

I have found an alternative solution by moving first to the parent node

XmlNode parentNode = doc.SelectSingleNode(parentNodePath);

Then by listing all the child nodes

XmlNodeList childNodes = doc.SelectNodes(childNodePath);

And I can move through the attributes with the iterator

for (int i = 0; i < childNodes.Count; i++)
{
    for (int j = 0; j < childNodes[i].Attributes.Count; j++)
    {
        childNodes[i].Attributes[j].Name = newName;
        childNodes[i].Attributes[j].Content = newContent;
    }
}