Master Pattern Matching in XML: Using LINQ Contains Method for SQL-Like Queries in C# and VB.NET

← PrevNext →

If you're familiar with SQL Server, you've likely used the SQL LIKE operator to search for specific patterns within your SQL tables. The LIKE operator employs the "%" (percent) sign to match patterns. However, when working with XML as your database and needing to search for a pattern, the approach differs. In this guide, I'll demonstrate how to achieve SQL-like pattern matching in XML using the LINQ Contains method in C#. By leveraging LINQ, you can efficiently navigate and query XML data to find the patterns you need.
See this demo

SQL LIKE example:

The following query uses the SQL LIKE operator in the "WHERE" clause to filter out employees whose designation contains the pattern "Acc".

SELECT * FROM dbo.Employee WHERE Designation LIKE '%Acc%'

LINQ Contains method

The LINQ Contains method takes two parameters. The first parameter is the value that you wish to use to search in the XML elements, and the second is the IEqualityComparer instance to compare the values.

Scenario

Let's consider a straightforward scenario. We have an XML document containing a list of books, each with an element named <ReleaseDate>. Our goal is to search for books released in a specific month. The date format used is dd-mm-yyyy, with numeric values separated by hyphens ( - ). By utilizing the LINQ Contains method, we can efficiently navigate through the XML and find the desired books based on their release dates.

The XML
<?xml version="1.0"?>
<Library>
  <List>
    <BookName>Computer Architecture</BookName>
    <Category>Computers</Category>
    <Price>125.60</Price>
    <ReleaseDate>02-10-2015</ReleaseDate>
  </List>
  <List>
    <BookName>Advanced Composite Materials</BookName>
    <Category>Science</Category>
    <Price>172.56</Price>
    <ReleaseDate>15-05-2015</ReleaseDate>
</List>
  <List>
    <BookName>Asp.Net 4 Blue Book</BookName>
    <Category>Programming</Category>
    <Price>56.00</Price>
    <ReleaseDate>06-10-2015</ReleaseDate>
  </List>
  <List>
    <BookName>Stategies Unplugged</BookName>
    <Category>Science</Category>
    <Price>99.99</Price>
    <ReleaseDate>11-08-2015</ReleaseDate>
  </List>
  <List>
    <BookName>Teaching Science</BookName>
    <Category>Science</Category>
    <Price>164.10</Price>
    <ReleaseDate>02-10-2015</ReleaseDate>
  </List>
  <List>
    <BookName>Challenging Times</BookName>
    <Category>Business</Category>
    <Price>150.70</Price>
    <ReleaseDate>11-08-2015</ReleaseDate>
  </List>
  <List>
    <BookName>Circuit Bending</BookName>
    <Category>Science</Category>
    <Price>112.00</Price>
    <ReleaseDate>07-02-2015</ReleaseDate>
  </List>
  <List>
    <BookName>Popular Science</BookName>
    <Category>Science</Category>
    <Price>210.40</Price>
    <ReleaseDate>21-08-2015</ReleaseDate>
  </List>
  <List>
    <BookName>ADOBE Premiere</BookName>
    <Category>Computers</Category>
    <Price>62.20</Price>
    <ReleaseDate>01-10-2015</ReleaseDate>
  </List>
</Library>
The Markup

In the markup section, I have an HTML select option, which has values from 01 to 12, for months of the year. Two more controls, a button to call a code behind procedure to search for the selected pattern in XML and a label control to display the result.

<div style="font:13px Verdana;">
                
    <%-- SEARCH A VALUE IN XML --%>
    <div style="width:300px;border:solid 1px #CCC;padding:10px;">
        <p>Select the Month:
            <select id="selMonth" runat="server">
                <option value="">-- SELECT --</option>
                <option value="01">01</option>
                <option value="02">02</option>
                <option value="03">03</option>
                <option value="04">04</option>
                <option value="05">05</option>
                <option value="06">06</option>
                <option value="07">07</option>
                <option value="08">08</option>
                <option value="09">09</option>
                <option value="10">10</option>
                <option value="11">11</option>
                <option value="12">12</option>
            </select>

            <button id="btSearch" onserverclick="search_text" runat="server">Search</button>
        </p>
        <p><label id="lblXML" runat="server"></label></p>
    </div>
</div>
Code behind (C#)
using System;
using System.Collections.Generic;   // FOR IEnumerable.

using System.Linq;                  // FOR Descendants().
using System.Xml.Linq;              // FOR XDocument, XElement.

public partial class SiteMaster : System.Web.UI.MasterPage
{
    protected void search_text(object sender, EventArgs e)
    {
        Search("-" + selMonth.Value + "-");
    }

    private void Search(string sVal)
    {
        // LOAD XML DOCUMENT.
        var xml_Doc = XDocument.Load(Server.MapPath("library.xml"));

        IEnumerable<XElement> search_date = null;
        search_date =
            (from xFi in xml_Doc.Descendants("List")
                where xFi.Element("ReleaseDate").Value.Contains(sVal)
                select xFi
            );
        
        // Display data.
        if ((search_date != null))
        {
            lblXML.InnerHtml = "";	// Clear the label for new data.
            foreach (XElement result in search_date)
            {
                lblXML.InnerHtml = 
                    lblXML.InnerHtml +
                "<b> Release Date: </b>" + result.Element("ReleaseDate").Value + "<br />" +
                "<b> Book Name: </b>" + result.Element("BookName").Value + "<br />" +
                "<b> Category: </b> " + result.Element("Category").Value + "<br />" +
                "<b> Price: </b>" + result.Element("Price").Value + "<br /><br />";
            }
        } 
    }
}
See this demo
Code behind (VB.NET)
Option Explicit On

Partial Class Site
    Inherits System.Web.UI.MasterPage

    Protected Sub search_text(ByVal sender As Object, ByVal e As EventArgs)
        Search("-" & Trim(selMonth.Value) & "-")
    End Sub

    Private Sub Search(ByVal sVal As String)

        ' LOAD XML DOCUMENT.
        Dim xml_Doc = XDocument.Load(Server.MapPath("library.xml"))

        Dim search_date As IEnumerable(Of XElement)
        search_date =
            (From xFi In xml_Doc.Descendants("List") _
                Where xFi.Element("ReleaseDate").Value.Contains(sVal)
            )

        ' NOW DISPLAY THE RESULT.
        If Not search_date Is Nothing Then
            lblXML.InnerHtml = ""

            For Each result As XElement In search_date
                lblXML.InnerHtml = lblXML.InnerHtml & _
                "<b> Release Date: </b>" & result.Element("ReleaseDate").Value & "<br />" & _
                "<b> Book Name: </b>" & result.Element("BookName").Value & "<br />" & _
                "<b> Category: </b> " & result.Element("Category").Value & "<br />" & _
                "<b> Price: </b> $" & result.Element("Price").Value & "<br /><br >"
            Next
        End If
        
        If Trim(lblXML.InnerHtml = "") Then
            lblXML.InnerHtml = "No data found"
        End If
    End Sub
End Class

The Contains method will search for a specified pattern in the XML doc. It internally loops through each element and look for a match of a value that we have specified as its parameter.

where xFi.Element("ReleaseDate").Value.Contains(sVal)

LINQ Methods "toLower()" and "toUpper()"

The values that I am passing as parameter in the above example are numeric. However, if the values are alphabets, then remember, XML is case sensitive. For example, if you searching for a pattern in the <Category> element that has values are in UPPER case and you are passing a value in LOWER case, then convert the parameter to lower using .toLower() method.

<Category>PROGRAMMING</Category>
C#
where xFi.Element("Category").Value.ToLower().Contains("programming")
VB.NET
Where xFi.Element("Category").Value.ToLower.Contains("programming")

Similarly, if it’s a reverse, that is, the values in XML in lower and the parameter is in upper case, then use .toUpper() method.

← PreviousNext →