Search code examples
xmlxmlreader

XMLReader - How to read this element/node pattern


This is some XML I need to be able to read. I need to get the QualitySetting values for each Feature assigned to some variables:

   <?xml version="1.0" encoding="UTF-8" ?>
   <GraphicsConfig>
      <FX>
       <Off>
           <LocalisationName>$QUALITY_OFF;</LocalisationName>
        <Item>
            <Feature>LightCones</Feature>
            <QualitySetting>0</QualitySetting>
        </Item>
        <Item>
            <Feature>LensFlares</Feature>
            <QualitySetting>0</QualitySetting>
        </Item>
        <Item>
            <Feature>Debris</Feature>
            <QualitySetting>0</QualitySetting>
        </Item>
        <Item>
            <Feature>ParticleEffects</Feature>
            <QualitySetting>0</QualitySetting>
        </Item>
        <Item>
            <Feature>Trails</Feature>
            <QualitySetting>0</QualitySetting>
        </Item>
        <Item>
            <Feature>Beams</Feature>
            <QualitySetting>0</QualitySetting>
        </Item>
        <Item>
            <Feature>Fog</Feature>
            <QualitySetting>0</QualitySetting>
        </Item>
    </Off>
   </FX>
 </GraphicsConfig>

I am using XMLReader and I usually use something like:

Dim guiSettings = XElement.Load(_localAppDataFilePath & "\Frontier Developments\Elite Dangerous\Options\Graphics\GraphicsConfigurationOverride.xml")
            target = guiSettings.Element("GUIColour").Elements("Default").[Single]()
            r = target.Element("MatrixRed").Value

Open to doing this other ways, and if there is no easy way to do this, I am perfectly happy to just locate the <Feature> element with the appropriate name and then simply just read the very next node, but thought I'd ask as I always want to improve my XML editing knowledge.


Solution

  • The code below uses a combination of XmlReader and XML Linq. I've posted this solution a lot of times. My preference in this case is not to use XmlReader instead use XML Linq to put results into a dictionary and posted that code as well. For huge XML files it is always best to use XmlReader to avoid out of memory error.

    I posted solution in both c# and vb.net


    Imports System.Xml
    Imports System.Xml.Linq
    Module Module1
    
        Const FILENAME As String = "c:\temp\test.xml"
        Sub Main()
            Dim reader As XmlReader = XmlReader.Create(FILENAME)
            Dim items As List(Of Item) = New List(Of Item)()
            While (Not reader.EOF)
    
                If reader.Name <> "item" Then
                    reader.ReadToFollowing("Item")
                End If
    
                If Not reader.EOF Then
    
                    Dim xItem As XElement = XElement.ReadFrom(reader)
                    Dim item As Item = New Item()
                    item.feature = xItem.Element("Feature")
                    item.setting = xItem.Element("QualitySetting")
    
                    items.Add(item)
                End If
            End While
    
            'using a dictionary 
            Dim doc As XDocument = XDocument.Load(FILENAME)
            Dim dict As Dictionary(Of String, String) = doc.Descendants("Item") _
                .GroupBy(Function(x) x.Element("Feature").ToString(), Function(y) y.Element("QualitySetting").ToString()) _
                .ToDictionary(Function(x) x.Key, Function(y) y.FirstOrDefault())
    
        End Sub
    
    End Module
    Public Class Item
    
        Public feature As String
        Public setting As String
    End Class
    

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Xml;
    using System.Xml.Linq;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            const string FILENAME = @"c:\temp\test.xml";
            static void Main(string[] args)
            {
                XmlReader reader = XmlReader.Create(FILENAME);
                List<Item> items = new List<Item>();
                while (!reader.EOF)
                {
                    if(reader.Name != "item")
                    {
                        reader.ReadToFollowing("Item");
                    }
                    if (!reader.EOF)
                    {
                        XElement xItem = (XElement)XElement.ReadFrom(reader);
                        Item item = new Item() {
                            feature = (string)xItem.Element("Feature"),
                            setting = (string)xItem.Element("QualitySetting")
                        };
    
                        items.Add(item);
                    }
                }
    
                //using a dictionary 
                XDocument doc = XDocument.Load(FILENAME);
                Dictionary<string, string> dict = doc.Descendants("Item")
                    .GroupBy(x => (string)x.Element("Feature"), y => (string)y.Element("QualitySetting"))
                    .ToDictionary(x => x.Key, y => y.FirstOrDefault());
            }
        }
        public class Item
        {
            public string feature { get; set; }
            public string setting { get; set; }
        }
    }