2

I have the following file

<container rev="1">
 <Folder name="root" access="all">
   <Folder name="aaa" access="user1">
     ...
   </Folder>
   <Folder name="bbb" access="user2">
     ...
   </Folder>
   <Folder name="ccc" access="user1">
     ...
   </Folder>
 ....
 </Folder>
</container>

A lot of folders encapsulated in container. The question is what is the best way to iterate through this folders checking folders name attribute? I have done this via Reader.skip() and readtofolowing (), but i dont like my solution as this folder blocks could be mixed. For example folder "xxx" is needed

taller
  • 17
  • 4
  • 1
    Hello, Welcome to SO. Please show us an example with the attempt. Please create full sample data set. It would be much easier if you created an example fiddle as well. – Train Jan 22 '20 at 15:46
  • sample in case of first folder - reader.ReadToFollowing("Folder");reader.ReadToFollowing("Item");if (reader.ReadToDescendant("Item")) { do {..some..logic...} while (reader.ReadToNextSibling("Item")); It works fine - i need the iteration between nodes of Folders – taller Jan 22 '20 at 15:49
  • Consider using XPath queries and/or LINQ to XML (`XElement`) to describe the results declaratively. Using a low-level XML reader and logic to skip nodes is much more involved and only really worth doing if performance tests show it's necessary. For example, simply getting all elements named "Folder" can be done with `//Folder`. – Jeroen Mostert Jan 22 '20 at 15:53
  • But I need only one, specific folder, not all – taller Jan 22 '20 at 16:00
  • Sorry I meant to ask if you could edit your question, as areproducable issue. This way we could give you a good answer. – Train Jan 22 '20 at 16:00
  • 2
    One specific folder can also be retrieved with XPath -- for example, the folder with name `xxx` is `//Folder[@name="xxx"]`, regardless of where it occurs in the document. XPath is a useful tool to have in your toolbox when dealing with XML, regardless of whether you end up using it. – Jeroen Mostert Jan 22 '20 at 16:02

1 Answers1

2

You just need a class that will store the data of a Folder node of the XML. Like this one:

public class Folder
{
    /// <summary>
    /// List of folders in the folder
    /// </summary>
    [XmlElement(ElementName = "Folder")]
    public List<Folder> Folders { get; set; }

    /// <summary>
    /// Name of the folder
    /// </summary>
    [XmlAttribute("name")]
    public string Name { get; set; }

    /// <summary>
    /// Access information of the folder
    /// </summary>
    [XmlAttribute("access")]
    public string Access { get; set; }

    public override string ToString()
    {
        var sb = new StringBuilder();
        ToString(sb, 0);
        return sb.ToString();
    }

    private void ToString(StringBuilder sb, int level)
    {
        const int indent = 2;
        sb.Append(new string(' ', level * indent));
        sb.AppendLine($"{Name} ({Access})");
        foreach (var folder in Folders)
            folder.ToString(sb, level + 1);
    }
}

An other for the container:

public class Container
{
    /// <summary>
    /// List of folders in the container
    /// </summary>
    [XmlElement(ElementName = "Folder")]
    public List<Folder> Folders { get; set; }

    /// <summary>
    /// Revision of the container
    /// </summary>
    [XmlAttribute("rev")]
    public string Revision { get; set; }

    public override string ToString()
    {
        var sb = new StringBuilder();
        sb.AppendLine($"Container rev: {Revision}");
        foreach (var folder in Folders)
            sb.AppendLine(folder.ToString());
        return sb.ToString();
    }
}

Use a XmlSerializer to de-serialize the file. To manage the container root node, use XmlRootAttribute as explained here.

Here the resulting program:

class Program
{
    static void Main(string[] args)
    {
        var rootAttribute = new XmlRootAttribute { ElementName = "container", IsNullable = true };
        var serializer = new XmlSerializer(typeof(Container), rootAttribute);

        using (var reader = new StreamReader("./input.xml"))
        {
            var result = (Container)serializer.Deserialize(reader);
            Console.WriteLine(result);
        }
    }
}

With this content in input.xml:

<container rev="1">
 <Folder name="root" access="all">
   <Folder name="aaa" access="user1">
     <Folder name="aab" access="user2" />
     <Folder name="aac" access="user2"/>
   </Folder>
   <Folder name="bbb" access="user2" />
   <Folder name="ccc" access="user1"/>
 </Folder>
</container>

The output is:

Container rev: 1
root (all)
  aaa (user1)
    aab (user2)
    aac (user2)
  bbb (user2)
  ccc (user1)
Orace
  • 7,822
  • 30
  • 45