How to use Linq to pull certain data from XmlNodeList and load it into a dictionary in C#

Viewed 493

I have some C# code which uses System.Xml to parse an xml file and then pull certain data from the xml into a dictionary.

The code below works, but its pretty messy and I don't really like it. I'd like to collapse it into some simple Linq calls, but I am still quite new to Linq and am not sure where to start. Can anyone offer some suggestions about how to Linq-ify this code? (preferably in method-syntax)

Also because the code writes the data to an external data structure (A dictionary) I don't know how that would work with Linq.

XML Sample Data:

<Localisation>

    <LOC_TAG value="tag_01" >
        <LOC_TEXT language="English" value="This is some text"/>
        <LOC_TEXT language="German" value="Das ist ein Text"/>
        <LOC_TEXT language="French" value="Il s'agit d'un texte"/>
    </LOC_TAG>

    <LOC_TAG value="tag_02" >
        <LOC_TEXT language="English" value="Another text string"/>
    </LOC_TAG>

</Localisation>

C# Code

Dictionary<string, string>  Strings = new Dictionary<string, string>();

XmlDocument xmlDoc = new XmlDocument(); 
xmlDoc.Load(new StringReader(locFile.text));

// Read each LOC_TAG and pull out its LOC_TEXT matching the required language into the dictionary
XmlNodeList locTags = xmlDoc.GetElementsByTagName("LOC_TAG");
foreach (XmlNode locTagNode in locTags)
{
    foreach (XmlNode child in locTagNode.ChildNodes)
    {
        if (child.Name == "LOC_TEXT" && 
            child.Attributes["language"].Value == Application.systemLanguage.ToString())
        {
            Strings.Add(locTagNode.Attributes["value"].Value, 
                        child.Attributes["value"].Value);
            break;
        }
    }
}

Any help much appreciated!

2 Answers

Here is the LINQ equivalent:

var language = Application.systemLanguage.ToString();
var strings = XElement.Parse(xml)
    .XPathSelectElements("./LOC_TAG")
    .Select(x => new 
    {
        Tag = x.Attribute("value").Value,
        Text = x.XPathSelectElement($"./LOC_TEXT[@language='{language}']")
            ?.Attribute("value").Value
    })
    .Where(x => x.Text != null)
    .ToDictionary(x => x.Tag, x => x.Text);

Since the query is iterating through XmlNodes, we can get the parent of current node at any time with tag.ParentNode, which makes the job easier.

var strings = (from tag in locTags.Cast<XmlNode>().SelectMany(t => t.ChildNodes.Cast<XmlNode>())
    where tag.Name == "LOC_TEXT" && tag.Attributes["language"].Value == Application.systemLanguage.ToString()
    select new { key = tag.ParentNode.Attributes["value"].Value, value = tag.Attributes["value"].Value })
    .ToDictionary(t=>t.key, t=>t.value);
Related