tags:

views:

6555

answers:

4

Greetings.

I'm looking for a way to parse a number of XML files in a particular directory with ASP.NET (C#). I'd like to be able to return content from particular elements, but before that, need to find those that have a certain value between an element.

Example XML file 1:

<file>
    <title>Title 1</title>
    <someContent>Content</someContent>
    <filter>filter</filter>
</file>

Example XML file 2:

<file>
    <title>Title 2</title>
    <someContent>Content</someContent>
    <filter>filter, different filter</filter>
</file>

Example case 1:

Give me all XML that has a filter of 'filter'.

Example case 2:

Give me all XML that has a title of 'Title 1'.

Looking, it seems this should be possible with LINQ, but I've only seen examples on how to do this when there is one XML file, not when there are multiples, such as in this case.

I would prefer that this be done on the server-side, so that I can cache on that end.

Functionality from any version of the .NET Framework can be used.

Thanks!

~James

+1  A: 

Use XPath?
http://www.w3schools.com/XPath/default.asp

Per Hornshøj-Schierbeck
+2  A: 

You might want to create your own iterator class that iterate over those files.

Say, make a XMLContentEnumerator : IEnumerable. that would iterate over files in a specific directory and parse its content, and then you would be able to make a normal LINQ filtering query such as:

var xc = new XMLContentEnumerator(@"C:\dir");

var filesWithHello = xc.Where(x => x.title.Contains("hello"));

I don't have the environment to provide a full example, but this should give some ideas.

chakrit
+6  A: 

If you are using .Net 3.5, this is extremely easy with LINQ:

//get the files
XElement xe1 = XElement.Load(string_file_path_1);
XElement xe2 = XElement.Load(string_file_path_2);

//Give me all XML that has a filter of 'filter'.
var filter_elements1 = from p in xe1.Descendants("filter") select p;
var filter_elements2 = from p in xe2.Descendants("filter") select p;
var filter_elements = filter_elements1.Union(filter_elements2);

//Give me all XML that has a title of 'Title 1'.
var title1 = from p in xe1.Descendants("title") where p.Value.Equals("Title 1") select p;
var title2 = from p in xe2.Descendants("title") where p.Value.Equals("Title 1") select p;
var titles = title1.Union(title2);

This can all be written shorthand and get you your results in just 4 lines total:

XElement xe1 = XElement.Load(string_file_path_1);
XElement xe2 = XElement.Load(string_file_path_2);
var _filter_elements = (from p1 in xe1.Descendants("filter") select p1).Union(from p2 in xe2.Descendants("filter") select p2);
var _titles = (from p1 in xe1.Descendants("title") where p1.Value.Equals("Title 1") select p1).Union(from p2 in xe2.Descendants("title") where p2.Value.Equals("Title 1") select p2);

These will all be IEnumerable lists, so they are super easy to work with:

foreach (var v in filter_elements)
    Response.Write("value of filter element" + v.Value + "<br />");

LINQ rules!

naspinski
+2  A: 

Here's one way using Framework 2.0. You can make this cleaner by using regular expressions rather than a simple string test. You can also try compiling your XPath expressions if you need to squeeze more for performance.

static void Main(string[] args)
{
    string[] myFiles = { @"C:\temp\XMLFile1.xml", 
                         @"C:\temp\XMLFile2.xml", 
                         @"C:\temp\XMLFile3.xml" };
    foreach (string file in myFiles)
    {
        System.Xml.XPath.XPathDocument myDoc = 
            new System.Xml.XPath.XPathDocument(file);
        System.Xml.XPath.XPathNavigator myNav = 
            myDoc.CreateNavigator();

        if(myNav.SelectSingleNode("/file/filter[1]") != null &&
            myNav.SelectSingleNode("/file/filter[1]").InnerXml.Contains("filter"))
            Console.WriteLine(file + " Contains 'filter'");

        if (myNav.SelectSingleNode("/file/title[1]") != null &&
            myNav.SelectSingleNode("/file/title[1]").InnerXml.Contains("Title 1"))
            Console.WriteLine(file + " Contains 'Title 1'");
    }

    Console.ReadLine();
}
Mattio