JohnC
JohnC

Reputation: 607

How to use a LINQ query to get XElement values when XElements have same name

I have a piece of xml like the following:

<Table>
  <Record>
    <Field>Value1_1</Field>
    <Field>Value1_2</Field>
  </Record>
  <Record>
    <Field>Value2_1</Field>
    <Field>Value2_2</Field>
  </Record>
</Table>

What i would like is a LINQ query that generates an IEnumerable that i can assign as the datasource of a DataGrid. What i have so far is as follows:

var temp = from record in table.Elements("Record")
            select record.Element("Field").Value

The fact that I can have multiple field elements is my stumbling block.

In the above example, what i need is something like an IEnumerable<string,string>. The datagrid would look something like this:

Value1_1, Value1_2
Value2_1, Value2_2

Upvotes: 9

Views: 61534

Answers (10)

R.Silva
R.Silva

Reputation: 11

Maybe this?

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

[TestMethod]
    public void Linq_XElement_Test()
    {


    string xml = @"<Table>
  <Record>
    <Field>Value1_1</Field>
    <Field>Value1_2</Field>
  </Record>
  <Record>
    <Field>Value2_1</Field>
    <Field>Value2_2</Field>
  </Record>
</Table>";

    XElement elements = XElement.Parse(xml);

    var qryRecords = from record in elements.Elements("Record")
                  select record;

    foreach (var rec in qryRecords)
    {
        Debug.WriteLine(rec.Value);
    }

    var qryFields = from record in elements.Elements("Record")
                 from fields in record.Elements("Field")
                 select fields;

    foreach (var fil in qryFields)
    {
        Debug.WriteLine(fil.Value);
    }

    IEnumerable<string> list = qryFields.Select(x => x.Value);

    foreach (string item in list)
    {
        Debug.WriteLine(item);
    }


}

Upvotes: 1

user166390
user166390

Reputation:

I don't get what the problem is and why a number of these answers look so complicated :-/ Here is my offering:

var r = (from record in table.Elements("Record")
         select (from element in record.Elements("Field")
                 select element.Value));

// => IEnumerable<IEnumerable<string>>

The inner IEnumerable is for the columns and the outer is for the rows. (The question is kind of confusing because there is no IEnumerable<T,T'>, the above is my adaptation of the intent.)

You could make the inner IEnumerable into a string (e.g. Join on ", "), but that's not very fun to put into a grid if you mean the values as columns!

var r = (from record in table.Elements("Record")
         select String.Join(", ",
             record.Elements("Field").Select(f => f.Value).ToArray());

// => IEnumerable<string>

The above could likely be made nicer if I actually knew LINQ Expressions. However, it works and covers the "other" case -- the "ToArray" is for .NET 3.5 and below.

Upvotes: 1

Maulik Patel
Maulik Patel

Reputation: 1

var detail1 = from d in ds.tbl_Looking_Fors where d.Profile_ID == id select d.Looking_For ;

            string[] datum = detail1.ToArray();
            if (datum != null && datum.Length > 0)
            {
                foreach (var row in datum)
                {
                    Label6.Text = datum[0]+" , "+datum[1];
                }

            }

Upvotes: 0

Jonas
Jonas

Reputation: 4584

Would an

IEnumerable<List<string>>

work? (not sure how to get that to display inline)

var recordList = from record in data.Elements("record") select record;
List<List<string>> x = new List<List<string>>(recordList.Count());
foreach (var record in recordList)
{
  var z = from field in record.Elements("field") select field.Value;
  x.Add(z.ToList());
}
return x.AsEnumerable();

Don't know if that works for your specific scenario or not.

Upvotes: 0

BenAlabaster
BenAlabaster

Reputation: 39836

Er... I think you're using the wrong LINQ paradigm for this problem. You should be using LINQ to XML which is slightly different maybe than you're expecting.

Check out this link:

http://msdn.microsoft.com/en-us/library/bb308960.aspx

It explains it using a structurally similar example to the one you presented.

Upvotes: 0

Alexander Kahoun
Alexander Kahoun

Reputation: 2488

I may be way off in what you're looking for, but are you looking for something like this?

        DataSet ds = new DataSet("Records DS");
        ds.Tables.Add("Records");
        foreach (XElement record in table.Descendants("Record"))
        {
            var temp = from r in record.Descendants("Field")
                       select r.Value;

            string[] datum = temp.ToArray();
            if (datum != null
                && datum.Length > 0)
            {
                foreach (string s in datum)
                    ds.Tables[0].Columns.Add();
                DataRow row = ds.Tables[0].NewRow();
                row.ItemArray = datum;
                ds.Tables[0].Rows.Add(row);
            }
        }

Then return ds and set the DataMember to "Records"

Upvotes: 0

Pita.O
Pita.O

Reputation: 1837

You need a 2-step processs:

Annotate the records (in lieu of attributes) and then Enumerate the fields and pass the values into an anonymous class for binding, like so:

string xml = [string-goes-here];

  XElement elem = XElement.Parse(xml);

  int count= 0;
  foreach(XElement recordElems in elem.Elements("Record")){
    recordElems.SetAttributeValue("id", count);
    count++;
  }
  var temp = from record in elem.Elements("Record")
             from field in record.Elements("Field")
             select new { Record = "Record " + record.Attribute("id").Value, Field = field.Value };

  foreach (var item in temp)
  {
    Console.WriteLine("{0}: {1}", item.Record, item.Field);
  }
}

Upvotes: 0

dss539
dss539

Reputation: 6950

It sounds like you want to denormalize the field so that it fits in 1 column in your data grid.

Does the following help?

var table = XElement.Parse(@"<Table>
                                <Record><Field>Value1_1</Field><Field>Value1_2</Field></Record>
                                <Record><Field>Value2_1</Field><Field>Value2_2</Field></Record>
                             </Table>");


var temp = from record in table.Elements("Record")
           from field in record.Elements("Field")
           group field.Value by record into groupedFields
           select groupedFields.Aggregate((l, r) => l + ", " + r);

foreach (var row in temp)
    Console.WriteLine(row);

Console.ReadKey();

Disclaimer: I don't do much SQL or LINQ anymore, so this probably could be made better. Feel free to change it.

Upvotes: 4

Richard Morgan
Richard Morgan

Reputation: 7681

Would something like this help?

var a = from record in table.Elements("Record")
    select new
    {
        one = (string)record.Elements().ElementAt(0),
        two = (string)record.Elements().ElementAt(1)
    };

Upvotes: 11

Bojan Resnik
Bojan Resnik

Reputation: 7378

You can chain calls to Elements():

var temp = from field in table.Elements("Record").Elements("Field")
           select field.Value;

You can also use Descendants():

 var temp = from field in table.Descendants("Field")
            select field.Value;

However, this will return all <Field> elements under <Table>, even if they are not within a <Record> element.

Upvotes: 0

Related Questions