1

Below is a sample of the type of XML file I am trying to handle. If I have only one part along with an accompanying number/character I can process the data extraction without the necessity of the 'if (!reader.EOF)' control structure. However when I try to include this structure so that I can loop back to checking for another part, number, and character group, it deadlocks.

Any advice as to how to do this properly? This was the most efficient idea that popped into my head. I am new to reading data from XMLs.

Sample Xml:

<?xml version="1.0" encoding="UTF-8"?>
<note>
  <part>100B</part>
  <number>45</number>
  <character>a</character>

  <part>100C</part>
  <number>55</number>
  <character>b</character>
</note>

Code:

String part = "part";
String number = "number";
String character = "character";
String appendString = "";
StringBuilder sb = new StringBuilder();

try
{
    XmlTextReader reader = new XmlTextReader("myPath");
    while (reader.Read())
    {
        switch (reader.NodeType)
        {
            case XmlNodeType.Element: // The node is an element.

                myLabel:
                if (reader.Name == part)
                {
                    part = reader.ReadInnerXml();
                }
                if (reader.Name == number)
                {
                    number = reader.ReadInnerXml();
                    number = double.Parse(number).ToString("F2"); //format num
                }
                if (reader.Name == character)
                {
                    character = reader.ReadInnerXml();
                }

                //new string
                appendString = ("Part: " + part + "\nNumber: " + number +
 "\nCharacter: " + character + "\n");

                //concatenate
                sb.AppendLine(appendString);

                if (reader.EOF != true)
                {
                    Debug.Log("!eof");
                    part = "part";
                    number = "number";
                    character = "character";
                    goto myLabel;
                }

                //print fully concatenated result
                sb.ToString();

                //reset string builder
                sb.Length = 0;

                break;
        }
    }
}
catch (XmlException e)
{
    // Write error.
    Debug.Log(e.Message);
}
catch (FileNotFoundException e)
{
    // Write error.
    Debug.Log(e);
}
catch(ArgumentException e)
{
    // Write error.
    Debug.Log(e);
}
Alexander Petrov
  • 13,457
  • 2
  • 20
  • 49
jtth
  • 876
  • 1
  • 12
  • 40
  • 2
    Any reason you want to use this approach rather than loading it into an `XDocument` for example? `XmlReader` is a very low-level approach. It's helpful if your XML won't fit into memory, but otherwise I'd generally use a higher level abstraction. – Jon Skeet Oct 04 '17 at 14:17
  • @JonSkeet Just looked up XDocuments and it appears they represent XML documents. Unsure of how this is better. I only want to parse through the data as presented. Do XDocs provide a more efficient way to do what I am trying to do? I am also not opposed to re-configuring the primitive format I am using. As I said I am new to XMLs. As such, this was the first* (or one of the first) formats google turned up for me. – jtth Oct 04 '17 at 14:24
  • 2
    They provide a much *easier* way of doing what you're trying to do. It's better because your code would be simpler. `XmlReader` is likely to be more computationally efficient, but I'd personally err on the side of simple code until you have concrete performance goals. – Jon Skeet Oct 04 '17 at 14:38
  • @JonSkeet Thank you for the advice. For right now, Alexanders example is exactly what I was looking for. – jtth Oct 04 '17 at 14:50
  • @JonSkeet Please see op! I figured I would update this thread rather then make a new post. If that is more appropriate please notify. – jtth Oct 17 '17 at 21:14
  • I don't see any change in the question, but if you're going to ask about something different, it would be better to do that in a new question. – Jon Skeet Oct 17 '17 at 21:15
  • @JonSkeet Just updated it! I reworded the title, but I am fine to make a new post if this is preferred. – jtth Oct 17 '17 at 21:20
  • Given that your edit is just an answer, it would have been better to put that into an *answer*. (It's fine to self-answer.) Personally I would use a `MemoryStream` to wrap the byte array, then create a `StreamReader` around that, mind you... – Jon Skeet Oct 18 '17 at 07:09
  • @Jon Skeet I'm a junior developer so I can tend to gravitate towards the first solution. Could you enlighten me as to why a MemoryStream wrapper and a StreamReader serve as a better solution as opposed to the StringReader I am using now? – jtth Oct 19 '17 at 23:05
  • It means you don't need to have the whole document in memory as a string. Basically it's more memory efficient than you current code. – Jon Skeet Oct 20 '17 at 05:52

2 Answers2

1

XmlReader class has many useful methods. Use it.

See this:

var sb = new StringBuilder();

using (var reader = XmlReader.Create("test.xml"))
{
    while (reader.ReadToFollowing("part"))
    {
        var part = reader.ReadElementContentAsString();
        sb.Append("Part: ").AppendLine(part);

        reader.ReadToFollowing("number");
        var number = reader.ReadElementContentAsDouble();
        sb.Append("Number: ").Append(number).AppendLine();

        reader.ReadToFollowing("character");
        var character = reader.ReadElementContentAsString();
        sb.Append("Character: ").AppendLine(character);
    }
}

Console.WriteLine(sb);
Alexander Petrov
  • 13,457
  • 2
  • 20
  • 49
0

Alexander's answer is fine, I just want to add sample using XDocument, according comments of Jon Skeet:

var sb = new StringBuilder();
var note = XDocument.Load("test.xml").Root.Descendants();            
foreach (var el in note)
{                
    sb.Append(el.Name).Append(": ").AppendLine(el.Value);               
}
Console.WriteLine(sb);
Alexan
  • 8,165
  • 14
  • 74
  • 101