As you may already know, if you need to programmatically access Sharepoint (2007/2010) data from a remote machine, the Sharepoint Object Model is not an option. Sharepoint provides a set of web service methods to expose its list data to third party applications (more details can be found here). While this is very nice and flexible, it forces you to perform some XML parsing and composing (CAML) in order to build the query and extract the results. Using the functions below, you can use a more object-oriented way to retrieve items.
Let's assume you have a custom list named "Products" that has an "ID", "Title", "SerialNumber", "Category" (lookup), "IsEnabled" (Yes/No), and "Owner" (person or group) fields. Before you do anything, a web reference that points to http://<server-url>/_vti_bin/lists.asmx should be added in your project (here we use the class name "WSS_List" for our web reference).
If you needed to query an item based on its title and serial number, using the wrapper functions this could be done like this:
Dim l As New WSS_List.Lists()
Dim params As New NameValueCollection
params.Add("Title", "Product A")
params.Add("SerialNumber", "4500058C")
Dim xQuery As XmlNode = MOSSQueryHelper.ConstructListQuery(params)
Dim xResults As XmlNode = l.GetListItems("Products", "", xQuery, Nothing, "", Nothing, "")
Dim Rows As List(Of NameValueCollection) = MOSSQueryHelper.ParseListResults(xResults, True)
For Each _row As NameValueCollection In Rows
Console.WriteLine("Product ID:" & _row("ID"))
Console.WriteLine("Product Title:" & _row("Title"))
Next
If you just needed a single Product based on its ID, the only parameter should be the ID value:
params.Add("ID", "2")
Note that the inputs must be defined in a NameValueCollection object, where [Name] is the Column and [Value] the actual value to compare. The results are returned as a collection of NameValueCollection objects, each one of which represent a single item/record.
Apart from simple textual fields, the class also supports queries based on lookup fields (they are treated differently in CAML). In the example above, if we wanted to find products based on the CategoryID, we should define our parameters as follows:
params.Add("Category", "5_{LOOKUPID}")
Yeah I know I know, it's quick and dirty, however you are free to modify it and make it even more structured according to your needs. A similar way is used to filter for a field of sharepoint type "Person or group" (provide user id) or "Yes/No" (provide 0 or 1). This time, the {USER} and {BOOLEAN} suffixes are used:
params.Add("Owner", "7_{USER}")
params.Add("IsEnabled", "1_{BOOLEAN}")
The full source code of the class in VB .NET is the following:
Imports System.Xml
Public Class MOSSQueryHelper
Public Shared Function ConstructListQuery(ByVal Arguments As NameValueCollection) As XmlNode
'THIS IS TESTED FOR UP TO 4 PARAMETERS
'use VALUE_{LOOKUPID} for lookup ids
Dim tmpStr As String = ""
tmpStr &= "<Query>"
If Arguments IsNot Nothing AndAlso Arguments.Count > 0 Then
tmpStr &= "<Where>"
For i As Integer = 1 To Arguments.Count
Dim arg As String = Arguments.Keys(i - 1)
If i = 1 Then
If Arguments.Count > 1 Then
tmpStr &= "<And>"
End If
If Arguments.Count > 2 Then
tmpStr &= "<And>"
End If
End If
If i = 3 Then
If Arguments.Count > 2 Then
tmpStr &= "</And>"
End If
If Arguments.Count > 3 Then
tmpStr &= "<And>"
End If
End If
tmpStr &= "<Eq>"
If Arguments(arg).EndsWith("_{LOOKUPID}") Then
tmpStr &= "<FieldRef Name=""" & arg & """ LookupId=""TRUE"" />"
Arguments(arg) = Arguments(arg).Replace("_{LOOKUPID}", "")
tmpStr &= "<Value Type=""Lookup"">" & Arguments(arg) & "</Value>"
ElseIf Arguments(arg).EndsWith("_{BOOLEAN}") Then
tmpStr &= "<FieldRef Name=""" & arg & """ />"
Arguments(arg) = Arguments(arg).Replace("_{BOOLEAN}", "")
tmpStr &= "<Value Type=""Boolean"">" & Arguments(arg) & "</Value>"
ElseIf Arguments(arg).EndsWith("_{USER}") Then
tmpStr &= "<FieldRef Name=""" & arg & """ LookupId=""TRUE"" />"
Arguments(arg) = Arguments(arg).Replace("_{USER}", "")
tmpStr &= "<Value Type=""User"">" & Arguments(arg) & "</Value>"
Else
tmpStr &= "<FieldRef Name=""" & arg & """ />"
tmpStr &= "<Value Type=""Text"">" & Arguments(arg) & "</Value>"
End If
tmpStr &= "</Eq>"
'last parameter
If i = Arguments.Count Then
If Arguments.Count > 3 Then
tmpStr &= "</And>"
End If
If Arguments.Count > 1 Then
tmpStr &= "</And>"
End If
End If
Next
tmpStr &= "</Where>"
End If
tmpStr &= "</Query>"
Dim tmpDoc As New XmlDocument
tmpDoc.LoadXml(tmpStr)
Return tmpDoc.DocumentElement
End Function
Public Shared Function ParseListResults(ByVal nd As XmlNode, Optional ByVal RemovePrefix As Boolean = True) As List(Of NameValueCollection)
Dim tmpList As New List(Of NameValueCollection)
'RETURNS:
'<listitems xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882"
'xmlns: dt = "uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
' xmlns:rs="urn:schemas-microsoft-com:rowset" xmlns:z="#RowsetSchema"
' xmlns="http://schemas.microsoft.com/sharepoint/soap/">
' <rs:data ItemCount="4">
' <z:row ows_Number_Field="6555.00000000000"
' ows_Created = "2003-06-18T03:41:09Z"
' ows_ID="3" ows_owshiddenversion="3" />
' <z:row ows_Number_Field="78905456.0000000"
' ows_Created = "2003-06-18T17:15:58Z"
' ows_ID="4" ows_owshiddenversion="2" />
' ...
' </rs:data>
'</listitems>
For Each child As XmlNode In nd.ChildNodes
If child.NodeType = XmlNodeType.Element Then
For Each attr As XmlNode In child.ChildNodes
If attr.Name = "z:row" Then
Dim tmp As New NameValueCollection
For Each attr2 As XmlAttribute In attr.Attributes
If RemovePrefix Then
tmp(attr2.Name.Replace("ows_", "")) = attr2.Value
Else
tmp(attr2.Name) = attr2.Value
End If
Next
tmpList.Add(tmp)
End If
Next
End If
Next
Return tmpList
End Function
End Class
That's it! Currently the above CAML builder serves relatively simple search scenarios, since it composes a correct CAML for up to 4 parameters, that are combined only in AND mode and the operators are only "equals". You can of course enrich it to support more operations (for example "OR" search or "Like"/"Greater Than" operators among the parameters and their values).
Happy CAMLing 
cc53a82a-67c8-4c54-9bb8-2b29b390def1|3|4.3