m1nkeh
m1nkeh

Reputation: 1397

Linq to Xml Value Pair

I have Xml that looks like...

    <root>
        <extension>
            <Obj>
                <Id>12345</Id>
                <NameValuePair>
                    <Name>Prop1</Name>
                    <Value>Value1</Value>
                </NameValuePair>
                <NameValuePair>
                    <Name>Prop2</Name>
                    <Value>Value2</Value>
                </NameValuePair>
                <NameValuePair>
                    <Name>Prop3</Name>
                    <Value>Value3</Value>
                </NameValuePair>
            </Obj>
<Obj>
                <Id>67890</Id>
                <NameValuePair>
                    <Name>Prop4</Name>
                    <Value>Value5</Value>
                </NameValuePair>
                <NameValuePair>
                    <Name>Prop5</Name>
                    <Value>Value5</Value>
                </NameValuePair>
                <NameValuePair>
                    <Name>Prop6</Name>
                    <Value>Value6</Value>
                </NameValuePair>
            </Obj>
        </extension>
    </root>

I am trying to use XDocument / Linq to XML to achieve..

Id: 12345
Prop1: Value1
Prop2: Value2
Prop3: Value3

Id: 67890
Prop1: Value1
Prop2: Value2
Prop3: Value3

I already know the Id that needs to be looked up, in the code here i have hardcoded it as "12345" as per the example... this is what i have so far...

    var val = xmlDoc.Element("extension").Elements("Obj")
                                                 .Where(i => (string)i.Element("Id") == "12345" &&
                                                             (string)i.Element("NameValuePair").Element("Name") == "Prop2")
                                                 .Select(i => (string)i.Element("NameValuePair").Element("Value")).FirstOrDefault();

What seems to be happening though is that each time i "try" to get a value... it just returns NULL..

Ultimately, I am trying to put it into an object such as...

internal class MyClass
{
    public string Id { get; internal set; }
    public string Prop1 { get; internal set; }
    public string Prop2 { get; internal set; }
    public string Prop3 { get; internal set; }
}   

Any tips?

Upvotes: 0

Views: 184

Answers (2)

jdweng
jdweng

Reputation: 34433

Use a dictionary instead of properties in the class. I did it with xml linq :

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

namespace ConsoleApplication74
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XDocument doc = XDocument.Load(FILENAME);

            List<MyClass> myClasses = doc.Descendants("Obj").Select(x => new MyClass() {
                Id = (int)x.Element("Id"),
                dict1 = x.Elements("NameValuePair").GroupBy(y => (string)y.Element("Name"), z => (string)z.Element("Value"))
                        .ToDictionary(y => y.Key, z => z.FirstOrDefault()),
                dict2 = x.Elements("NameValuePair").GroupBy(y => (string)y.Element("Name"), z => (string)z.Element("Value"))
                        .ToDictionary(y => y.Key, z => z.ToList())
            }).ToList();

        }
    }
    internal class MyClass
    {
        public int Id { get; internal set; }
        public Dictionary<string,string> dict1 { get; set; }
        public Dictionary<string, List<string>> dict2 { get; set; }
    } 
}

Upvotes: 1

George Alexandria
George Alexandria

Reputation: 2936

You can convert your values to Lookup if for the same Prop you have multiple Value

        // use temporary variable to increase performance of item.Element, I'm not sure that all 'Obj' element contains 'Id'
        XElement idElement = null;
        var values = doc.Descendants("Obj")
            .Where(item => (idElement = item.Element("Id")) != null && idElement.Value == "12345")
            .Descendants("NameValuePair")
            .ToLookup(o => o.Element("Name").Value, o => new { Id = idElement.Value, Value = o.Element("Value").Value });

        foreach (var valuesByProp in values)
        {
            foreach (var prop in valuesByProp)
            {
                Console.WriteLine($"{prop.Id}, {valuesByProp.Key}, {prop.Value}");
            }
        }

Or you can directly group all NameValuePair by Id and then group by Prop

        var values = doc.Descendants("Obj")
            .ToLookup(o => o.Element("Id").Value, 
                o => o.Descendants("NameValuePair").ToLookup(p => p.Element("Name").Value, p => p.Element("Value").Value));

        foreach (var groupById in values)
        {
            foreach (var groupByProp in groupById)
            {
                foreach (var valuesByProp in groupByProp)
                {
                    foreach (var value in valuesByProp)
                    {
                        Console.WriteLine($"{groupById.Key}, {valuesByProp.Key}, {value}");
                    }
                }
            }
        }

Upvotes: 0

Related Questions