I was recently posed with the question of managing some of our HR open positions using SharePoint while having the ability to display these open positions on our external website. This sent me down a path that has given me some great understanding of the SharePoint Lists Web Service.
The first thing I wanted to do is make sure that I don't spend a lot of time recreating classes for use on this project. I imagine that at some point in the future someone else will approach me about presenting a list of their data on our external web site (or for use by an internal application). So, the first goal is to get as much reuse as possible. This is where I discovered some of the power of Generics for the first time.
As usual, I started with a search of the web and discovered this article. This was exceptional information and thus allowed me to begin to understand how I might accomplish my mission.
So, following the pattern of this article I assembled a SharepointListItems class that would be the basis for all sharepoint list data retrieved from the Lists Web Service (http://server/_vti_bin/lists.asmx). Here is the code for the SharepointListItems class (it's in VB because this is our language at work)
SharepointListItems.vb
Imports System.Xml
Imports System.Xml.Serialization
Imports System.Text
Imports System.Collections.Generic
Imports System
<XmlRoot(ElementName:="listitems", Namespace:="http://schemas.microsoft.com/sharepoint/soap/")> _
Public Class SharepointListItems(Of T As BaseListItem)
Private rows As RowData(Of T) = Nothing
<XmlElement(ElementName:="data", Namespace:="urn:schemas-microsoft-com:rowset")> _
Public Property RowData() As RowData(Of T)
Get
Return rows
End Get
Set(ByVal value As RowData(Of T))
rows = value
End Set
End Property
Public Shared Function FromXml(ByVal xmlNode As String) As SharepointListItems(Of T)
Dim xs As New XmlSerializer(GetType(SharepointListItems(Of T)))
Dim xtr As New XmlTextReader(xmlNode, XmlNodeType.Element, Nothing)
Dim o As Object = xs.Deserialize(xtr)
xtr.Close()
Return DirectCast(o, SharepointListItems(Of T))
End Function
End Class
All of the work for this class is being handled through the shared function FromXML. This allows me to make a simple method call to get my results as a group of objects.
Next, I built the RowData class to hold a reference for the <rs:data> element. This class is also a generic class so I can hold a specified list of items
RowData.vb
Imports System.Xml
Imports System.Xml.Serialization
Imports System.Text
Imports System.Collections.Generic
Imports System
<XmlRoot(ElementName:="listitems", Namespace:="http://schemas.microsoft.com/sharepoint/soap/")> _
Public Class SharepointListItems(Of T As BaseListItem)
Private rows As RowData(Of T) = Nothing
<XmlElement(ElementName:="data", Namespace:="urn:schemas-microsoft-com:rowset")> _
Public Property RowData() As RowData(Of T)
Get
Return rows
End Get
Set(ByVal value As RowData(Of T))
rows = value
End Set
End Property
Public Shared Function FromXml(ByVal xmlNode As String) As SharepointListItems(Of T)
Dim xs As New XmlSerializer(GetType(SharepointListItems(Of T)))
Dim xtr As New XmlTextReader(xmlNode, XmlNodeType.Element, Nothing)
Dim o As Object = xs.Deserialize(xtr)
xtr.Close()
Return DirectCast(o, SharepointListItems(Of T))
End Function
End Class
Finally, I needed my base class that I would use to capture common list items. This class uses the XmlAttribute attribute to map the contents of the returned Xml to the object's properties.
BaseListItem.vb
Imports System.Xml
Imports System.Xml.Serialization
Imports System.Text
Imports System.Collections.Generic
Imports System
Public Class BaseListItem
Protected _id As Integer = 0
Protected _attachments As Integer = 0
Protected _hiddenVersion As Integer = 0
Protected _linkTitle As String = String.Empty
Protected _title As String = String.Empty
Public Sub New()
End Sub
<XmlAttribute("ows_Title")> _
Public Property Title() As String
Get
Return _title
End Get
Set(ByVal value As String)
_title = value
End Set
End Property
<XmlAttribute("ows_Attachments")> _
Public Property AttachmentCount() As Integer
Get
Return _attachments
End Get
Set(ByVal value As Integer)
_attachments = value
End Set
End Property
<XmlAttribute("ows_owshiddenversion")> _
Public Property HiddenVersion() As Integer
Get
Return _hiddenVersion
End Get
Set(ByVal value As Integer)
_hiddenVersion = value
End Set
End Property
<XmlAttribute("ows_ID")> _
Public Property ID() As Integer
Get
Return _id
End Get
Set(ByVal value As Integer)
_id = value
End Set
End Property
<XmlAttribute("ows_LinkTitle")> _
Public Property LinkTitle() As String
Get
Return _linkTitle
End Get
Set(ByVal value As String)
_linkTitle = value
End Set
End Property
End Class
Now I am ready to begin my project of importing job listings from my HR site into objects that I can use on the external website (or other applications). This object inherits it's functionality from the BaseListItems class and extends the functionality to include items specific to this list.
JobListing.vb
Imports EAS.SharepointListWebService
Public Class JobListing
Inherits BaseListItem
Private _PositionType As String
Private _PostingDepartment As String
Private _Description As String
Private _ContactPerson As String
Public Sub New()
End Sub
<XmlAttribute("ows_Position Type")> _
Public Property PositionType() As String
Get
Return _PositionType
End Get
Set(ByVal value As String)
_PositionType = value
End Set
End Property
<XmlAttribute("ows_Posting Department")> _
Public Property Department() As String
Get
Return _PostingDepartment
End Get
Set(ByVal value As String)
_PostingDepartment = value
End Set
End Property
<XmlAttribute("ows_Description")> _
Public Property Description() As String
Get
Return _Description
End Get
Set(ByVal value As String)
_Description = value
End Set
End Property
<XmlAttribute("ows_Contact Person")> _
Public Property ContactPerson() As String
Get
Return _ContactPerson
End Get
Set(ByVal value As String)
_ContactPerson = value
End Set
End Property
End Class
NOTE: I discovered that the _x0020_ convention used for the returned attributes will actually cause problems. If you'll notice, I have some attributes that look like ows_Contact Person instead of the expected ows_Contact_x0020_Person.
Now for the implementation of the code. Here's how I use the code in my application to populate a group of objects.
GetData Function
Private Sub GetJobData(ByVal username As String, ByVal password As String)
Dim CAMLDoc As New XmlDocument
Dim ndQuery As XmlNode = CAMLDoc.CreateElement("Query")
Dim HR As New HRListsWebService.Lists
ndQuery.InnerXml = "<Where>" & _
"<And>" & _
"<Eq>" & _
"<Value Type='Text'>Open - Approved</Value>" & _
"<FieldRef Name='Open'></FieldRef>" & _
"</Eq>" & _
"<Eq>" & _
"<Value Type='Integer'>1</Value>" & _
"<FieldRef Name='List_x0020_On_x0020_External_x00'></FieldRef>" & _
"</Eq>" & _
"</And>" & _
"<OrderBy>" & _
"<FieldRef Ascending='TRUE' Name='Posting Department'/>" & _
"</OrderBy>" & _
"</Where>"
Dim results As SharepointListItems(Of JobListing)
Try
HR.Credentials = New System.Net.NetworkCredential(userName, password)
Dim ndListItems As XmlNode = HR.GetListItems("Open Positions List", String.Empty, ndQuery, Nothing, String.Empty, Nothing, Nothing)
results = SharepointListItems(Of JobListing).FromXml(ndListItems.OuterXml)
Catch ex As Exception
Throw New Exception("Unsuccessful query to the SharePoint web service.", ex)
End Try
_JobListItems = results
End Sub
I hope this helps someone out there working with lists. I'm open to suggestions as I'm still learning how this really works!

No comments:
Post a Comment