using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Data;
using System.Net;
namespace GetSPSListItem
{
class Program
{
static void Main( string[] args)
{
// 测试机 "a9d594da-3cda-4420-badd-366478991b41"
// 正式机 "a9d594da-3cda-4420-badd-366478991b41"
string webID = " a9d594da-3cda-4420-badd-366478991b41 ";
// 测试机 http://cnsh-10apl1.hi-p.com/ITPortal/_vti_bin/Lists.asmx
// 正式机 http://cnsh-10its1.hi-p.com/ITPortal/_vti_bin/Lists.asmx
string url = " http://cnsh-10apl1.hi-p.com/ITPortal/_vti_bin/Lists.asmx ";
Lists.Lists listService = new Lists.Lists();
listService.Credentials = new NetworkCredential( " sharepoint ", " google.. ", "xxx ");
listService.Url = url;
// listService.Credentials = System.Net.CredentialCache.DefaultCredentials;
XmlDocument xmlDoc = new System.Xml.XmlDocument();
XmlNode ndQuery = xmlDoc.CreateNode(XmlNodeType.Element, " Query ", "");
XmlNode ndViewFields =
xmlDoc.CreateNode(XmlNodeType.Element, " ViewFields ", "");
XmlNode ndQueryOptions =
xmlDoc.CreateNode(XmlNodeType.Element, " QueryOptions ", "");
ndQueryOptions.InnerXml =
" <IncludeMandatoryColumns>FALSE</IncludeMandatoryColumns> " +
" <DateInUtc>TRUE</DateInUtc> ";
// ndViewFields.InnerXml = @"
// <FieldRef Name='Title' />
// <FieldRef Name='Project_x0020_Number'/>";
ndViewFields.InnerXml = "";
// ndQuery.InnerXml = @"<Where><And><Gt><FieldRef Name='Field1'/>" +
// "<Value Type='Number'>5000</Value></Gt><Gt><FieldRef Name='Field2'/>" +
// "<Value Type='DateTime'>2003-07-03T00:00:00</Value></Gt></And></Where>";
try
{
XmlNode ndListItems = listService.GetListItems( " ListName ", null, ndQuery, ndViewFields, null, ndQueryOptions, webID);
XmlTextReader xr = new XmlTextReader(ndListItems.OuterXml, XmlNodeType.Element, null);
DataSet ds = new DataSet();
ds.ReadXml(xr);
Console.WriteLine(ndListItems.OuterXml);
}
catch (System.Web.Services.Protocols.SoapException ex)
{
Console.WriteLine( " Message:\n " + ex.Message + " \nDetail:\n " +
ex.Detail.InnerText +
" \nStackTrace:\n " + ex.StackTrace);
}
Console.ReadLine();
}
}
}
运行后Dataset中Row表内容如下
参照http://weblogs.asp.net/paulballard/archive/2005/05/08/Using-Data-From-SharePoint-2003-Lists.aspx
http://msdn.microsoft.com/en-us/library/lists.lists.getlistitems(v=office.12).aspx
http://www.kuqin.com/dotnet/20080619/9711.html
SharePoint通过一系列Web Services来支持互操作和远程调用,这里的SharePoint,我指的是WSS 3.0(Windows SharePoint Services)和MOSS 2007(Microsoft Office SharePoint Server)。有好几种方法来编程访问SharePoint,比如对象模型、Web Services、RPC over HTTP、以及WebDAV,所有这些方式基于应用程序的需要都有它们各自的优点和适用范围。SharePoint RPC协议很有用也很强大,特别在添加内容到服务器的时候,它确实有它的优势。对象模型比Web Service更健全特性更丰富,但是它不能提供远程操作的支持,因此若想使用对象模型,你的代码必须运行在SharePoint服务器上,并且你必须使用.NET语言来开发。之所以要进行SharePoint数据的远程操作,有几个不同方面的原因,无论是管理脚本,还是诸如运行在客户端机器上需要访问 SharePoint内容的应用程序,或者在企业中的非SharePoint应用程序,都需要对SharePoint进行原创操作。SharePoint 的Web Services构建于对象模型之上,暴露了对象模型里可用特性的一个子集,允许任何能“消费”Web Services的语言和平台进行远程操作。对于大部分任务,SharePoint所提供的Web Services都能满足,但是你也会发现一些对象模型中必需的操作没有在Web Services中出现,其要用自定义Web Service实现的方式来扩展。在这篇文章中,我们将会谈到在WSS3和MOSS 2007中存在的一些开箱即用的Web Services,并讲述如何从Java和.NET中“消费”它们。
Web Services构建概览
我不会过于深入WSS和MOSS之间的不同点,已经有大量的文章很好地解释了这个东西了,我们这里需要关心的只是Web Services的区别。记住MOSS 2007是构建于WSS 3.0之上的,其有构建于ASP.NET,并运行于IIS(Internet Information Server)中。对IIS和ASP.NET有深入的了解是重要的,其有助于理解和使用SharePoint。我们将要讨论的大部分SharePoint 的Web Services是包含于WSS 3.0中,但也有一部分额外的是包含于MOSS 2007中,其利用了MOSS 2007所提供的额外特性,如更高级的企业搜索特性。
SharePoint Web Services使用ASP.NET Web Services (ASMX)来实现,所以你会在"web server extensions12ISAPI"(其通常位于C:Program FilesCommon FilesMicrosoft Sharedweb server extensions12ISAPI")之下的"Microsoft Shared"目录中找到这些Web Services中大部分的物理文件。用于管理中心工具的管理Web Service位于ADMISAPI文件夹中,其在管理中心控制台里是一个名为"_vti_adm"的虚拟目录。当你创建了一个SharePoint站点时,它将包含一个名为"_vti_bin"的虚拟目录,以指向这个位置。IIS不为子站点包含任何应用程序或虚拟目录,它们只是包含通过 SharePoint元数据和HttpModules实现的对_vti_bin虚拟目录的映射。
对于任何服务,你都能找到一个*.wsdl.aspx文件,其可以生成服务的WSDL(Web Services Description Language),一个*.disco.aspx文件提供访问发现实现,以及一个实际的*.asmx端点文件,它们中的大部分只是包含了一个引用到实际实现服务功能的SharePoint程序集和类型的页面指示符。
服务文件类型 | 描述 |
*.wsdl.aspx (search.wsdl.aspx) | 在SharePoint中生成WSDL(Web Services Description Language)的文件 |
*.disco.aspx (seach.disco.aspx) | 提供发现实现的文件 |
*.asmx (search.asmx) | Web Services端点文件,它们中大部分只是包含一个引用到实际实现服务功能的SharePoint程序集和类型的页面指示符 |
开箱即用的服务
在SharePoint中,有很多Web Services是以OOTB(开箱即用,Out Of The Box)的方式实现的,这可以完成大部分通用而基本的任务:从管理任务到搜索,到处理列表数据等等。下面是一个可供你引用的SharePoint Web Services的列表,和它们的大致解释。类似这样的简单列表通过让我对所有的服务和方法有一个快速的了解,从而帮助我更好的使用这些Web Services。
服务 | 管理用途 |
(_vti_adm/Admin.asmx) | 创建删除站点的管理方法,获取被部署的SharePoint的语言CreateSiteDeleteSiteGetLanguageRefreshConfigCache |
通知 (Alerts.asmx) | 用于操作SharePoint通知列表项的方法DeleteAlertsGetAlerts |
验证 (Authentication.asmx) | 客户端代理为使用窗体验证的站点提供用户验证的方法Login (用于验证)Mode (返回当前站点的验证模式) |
复制 (Copy.asmx) | 在站点之间或内部复制文件的方法CopyIntoItems(把文档以字节数组的形式复制到服务器上的某个位置)CopyIntoItemsLocal (在同一个服务器上把文档从一个位置复制到另外一个位置上)GetItem(创建文档的字节数组格式,以传递给CopyIntoItems方法) |
文档工作区 (Dws.asmx) | 管理文档工作区站点和数据的方法CanCreateDwsUrlCreateDwsCreateFolderFindDwsDocGetDwsDataGetDwsMetaDataRemoveDwsUserRenameDwsUpdateDwsData |
窗体 (Forms.asmx) | 当工作于某个列表内容时,返回用在这个用户界面中的窗体信息的方法GetFormGetFormCollection |
图像 (Imaging.asmx) | 创建和管理图片库的方法CheckSubwebAndListCreateNewFolderDeleteDownloadEditGetItemsByIdsGetItemsXMLDataGetListItemsListPictureLibraryRenameUpload |
获取列表数据 (DspSts.asmx) | 完成对站点和SharePoint中的列表的查询Query(完成对SharePoint列表和站点的查询) |
列表 (Lists.asmx) | 对列表和列表数据进行操作的方法
|
会议 (Meetings.asmx) | 创建和管理会议工作区站点
|
人员 (People.asmx) | 解析和查找安全主体
|
权限 (Permissions.asmx) | 处理站点或列表权限的方法
|
目录管理 (sharepointemailws.asmx) | 管理活动目录e-mail分发组和他们的成员信息的方法
|
站点数据 (SiteData.asmx) | 从站点或列表中返回元数据或列表数据的方法
|
站点 (Sites.asmx) | 返回关于站点集和站点模板的信息的方法
|
搜索 (spsearch.asmx) | 使用搜索服务进行搜索的方法
|
用户和用户组 (usergroup.asmx) | 操作用户角色定义和用户组的方法
|
版本 (Versions.asmx) | 操作文件版本的方法
|
视图 (Views.asmx) | 操作列表视图的方法
|
Web 部件页 (WebPartPages.asmx) | 操作Web 部件页的方法
|
Web应用程序 (Webs.asmx) | 操作站点和子站点的方法CreateContentTypeCustomizeCssDeleteContentTypeGetActivatedFeaturesGetAllSubWebCollectionGetColumnsGetContentTypeGetContentTypesGetCustomizedPageStatusGetListTemplatesGetWebGetWebCollectionRemoveContentTypeXmlDocumentRevertAllFileContentStreamsRevertCssRevertFileContentStreamUpdateColumnsUpdateContentTypeUpdateContentTypeXmlDocumentWebUrlFromPageUrl |
MOSS搜索 (Search.asmx) | 通过MOSS(Microsoft Office SharePoint Server)搜索服务进行搜索的方法,它们也包含获取被管理搜索属性的方法
|
消费查询服务
现在,我们已经对SharePoint Web Services有了初步的了解,以其通读用户手册还不如踢踢轮胎就开车上路了【译者注:意指先实际操作一下比读完帮助手册好】。我们没有太多时间来深入这些Web Services的所有方面,这些将会在下一篇文章中来详细讨论。基于某些原因,SharePoint的很多Web Services不是返回XML Node就是XML编码字符串,不过不幸的是,它并不能返回类型化的Schema,因此相关工具就不能通过分析来自动生成更好的代理对象了。我曾经看到过使用字符串格式的实现来解决类型化结构的缺陷,也花了很多时间和精力来找出可以描述这些服务传入和返回的XML的一种Schema。我最终创建了 queryresponse.xsd,它是我综合了文档和服务传入和返回XML的信息后,推断出的一种Schema;它允许我使用.NET中的 xsd.exe及java中的JAXB来生成代理类。
注意:我们之前讨论了两个不同的搜索Web Services,MOSS中的search.asmx和WSS中的spsearch.asmx。这两个服务在Schema方面非常相似,但尝试对运行 MOSS的机器调用spsearch.asmx,会得到一个异常,这是由于WSS索引和查询服务被禁用了,其已经被MOSS中更高级的搜索特性所替代了。
使用.NET的例子
从.NET中消费SharePoint Web Services是相当简单而直接的,你只需简单地添加Web引用,并使用生成的代理。不过,我们将多做一步来用XSD生成一组表示查询和应答的类,其能序列化出发送和返回的XML。
配置Visual Studio 2005来添加XSD.exe到菜单中
Visual Studio包含了一个名为XSD.exe的工具,其能被用于根据XSD生成.NET类型。不过这是一个命令行工具,但我喜欢把他添加为Visual Studio的一个菜单选项,以便能在IDE中执行它。如果你已经完成了这个步骤,或者你已经有一些添加插件这样的经验,你就可以跳过这步。
- 打开Visual Studio的“Tools”菜单上的“External Tools”对话框
- 点击“Add”
- 设置需要的属性并点击“OK”
属性 | 值 |
Command | XSD >> CSharp |
Title | C:Program FilesMicrosoft Visual Studio 8SDKv2.0Binxsd.exe |
Arguments | $(ItemPath) /c |
Initial Directory | $(ItemDir) |
Use Output Window | Checked |
下载XSD文件
从这里下载用于生成处理请求和响应过程中序列化成和反序列化的代理类的XSD文件。把下载的文件解压到工作目录;这个文件也包含了和完整的项目一起的XSD。
创建一个项目,并从XSD中生成.NET类型。现在我们已经有了XSD,我们将创建一个Windows Forms项目,并从下载的XSD中生成.NET类型。在打开Microsft.Search.Query.xsd之后,到工具菜单中选择我们上一步创建的XSD菜单项,以生成Microsoft_Search_Query.cs文件。
添加Web引用
我们现在可以通过在“solution explorer”右键点击我们项目的“references”节点,来添加针对搜索Web Service的Web引用了。这里我们将选择“Add Web Reference”,在URL文本框中输入“http:///_vti_bin /spsearch.asmx”,然后点击“Go”。可能会提示你登录到SharePoint服务器,随后即可得到这个服务提供的方法列表。设置“Web Reference Name”为“SPSearch”,并点击“Add Reference”。
生成代理子类
为了进一步简化开发,我们将生成我们自己的代理子类,以便我们能使用我们从XSD生成的类来包装QueryService,以隐藏Query方法传递和返回的XML的验证、序列化和反序列化工作。
创建调用服务的应用程序
现在,我们可以简单地实例化一个查询类和一个QueryService类,然后传递查询对象给QueryService类上的Query方法。
SPSearch.QueryService search = new SPSearch.QueryService();
Microsoft_Query_Request request = new Microsoft_Query_Request();
search.Query(request);
使用Java的例子
通过Java来消费SharePoint的Web Services,没有太大的不同;基础步骤基本是相同的,从我提供的XSD中生成我们的类,生成Web Services 代理,并构建应用程序。通过Java来消费SharePoint的Web Services,你所要面对的大部分挑战是登录验证和SharePoint的Web Services中的DataSet的使用。有大量的Java IDE可以简化Web Services的操作,但是由于我目前我主要工作于.NET中,所以我将演示一个使用SDK的例子,而在这个例子中对于IDE选择只好留给你了。
注意:: 这个例子是使用J2SE 6.0 Update 3来构建和测试的。
Java SharePoint示例项目目录和根文件
完整的项目和生成文件包含了一些批处理文件以便构建和运行这个例子。你需要编辑"xjc-build.bat"和"build-run.bat",设置其中的HOME变量为你的JDK的安装目录,以便能正常运行这些批处理文件;另外,在构建和运行示例之前,还要使用你的服务器配置信息(端点、用户名和密码)来编辑 wsspsample/Main.java。
导入服务的WSDL
由于几个原因,我一开始就把WSDL从我的SharePoint服务器上取到了我的开发机器上。首先,我需要编辑使用ADO.NET数据集的任何 WSDL,以使相关工具能正确处理它。其次,访问位于SharePoint服务器上的WSDL一般需要进行验证,而Java生成的代理在构造器中就需要访问WSDL,这会为我带来一些验证方面的问题。
获取搜索服务的WSDL
在Internet Explorer打开Windows SharePoint Services 3.0的搜索服务,并导航到“Error! Hyperlink reference not valid”上,然后从File菜单中选择Save As...,把这个这个页面保存到工作目录中,命名为spsearch.wsdl。如果你工作于MOSS 2007,那么你将使用“Error! Hyperlink reference not valid”。
编辑搜索服务的WSDL
MOSS 2007的QueryEx方法和GetSearchMetaData方法都返回了ADO.NET数据集。ADO.NET数据集是动态包装和表示WSDL中对Schema属性的循环引用的,所以这会引起我们使用的JAXB wsimport工具的一些问题。我发现这个问题在Java JDK以前的版本能正常处理,反而我现在使用的这个版本不正常。不过,我选择通过在记事本中编辑WSDL来解决这个问题,就是在“element”元素中查找ref为"s:schema的"实例,然后删除循环引用并保留Schema中的“any”元素。
<s:sequence>
<s:element ref="s:schema"/>
<s:any/>
</s:sequence>
被更改为如下格式:
<s:sequence>
<s:any minOccurs="0" maxOccurs="unbounded"/>
</s:sequence>
导入WSDL
现在,我们已经有一个本地的WSDL文件了,并进行了恰当的修改,这样我们就可以使用JAXB的wsimport.exe工具来生成代理类了。
wsimport -p wsspsample.webref.spsearch -keep spsearch.wsdl
wsimport -p wsspsample.webref.search -keep search.wsdl
我们使用-keep标志,在导入的时候可以保留java代码以便我们能为我们的应用程序调整生成的代码。这个工具使用绝对路径来生成代码,所以如果你打算移动应用程序到不同的目录下,并把WSDL和应用程序一起进行保存,那么我们需要编辑"webrefsearch QueryService.java"的Url,并自己构造它。为了解决这个问题,我们只需简单地使用相对路径来代替绝对路径。
url = new URL("file:search.wsdl");
生成请求响应类
为了生成请求和响应类,你需要使用4个我创建好的"Microsoft.Search"XSD文件,以及JAXB的xjc.exe工具。在下面,我们取消了包级别标注的生成过程,而是指定了一个目标包,并用这个工具创建出我们需要的类。
xjc -npa -p wsspsample.xom.query -d . Microsoft.Search.Query.xsd
xjc -npa -p wsspsample.xom.response -d . Microsoft.Search.Response.xsd
使用生成的类
我们已经有了代表传递到和从QueryService服务的Query方法返回的查询和响应数据包的类,以及Query服务的代理。现在,是时候来把它们用于应用程序里了。我们需要做的第一件事情是创建QueryService的实例,这个对象的构造器将获取我们本地的WSDL副本,并加载其中的设置。从 QueryService实例中,我们能通过getQueryServiceSoap方法获取到QueryServiceSoap的实例。注意,你需要保持getQueryServiceSoap的实例,并在我们处理qsp变量的时候使用它,这是因为getQueryServiceSoap方法总是会返回新的实例。在这里我们能编辑端点地址,以便实现比WSD所包含功能更多的事情,这样可以让我们的应用程序更加灵活,从而允许这个应用程序能连接到WSDL 中未设置的其他SharePoint站点和子站点上。
QueryService qs = new QueryService();
QueryServiceSoap qsp = qs.getQueryServiceSoap();
BindingProvider bp = (BindingProvider)qsp;
//bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "Administrator");
//bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "pass@word1");
bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
"http://barbie/_vti_bin/spsearch.asmx");
现在,我们创建序列化查询数据包和反序列化响应数据包的方法。
public static String SerializeQuery(QueryPacket qp)
{
try
{
JAXBContext jc =JAXBContext.newInstance("wsspsample.xom.query");
Marshaller ma = jc.createMarshaller();
ma.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,Boolean.TRUE);
ByteArrayOutputStream os = new ByteArrayOutputStream();
ma.marshal(qp, os);
return os.toString();
}
catch(JAXBException ex)
{
return "";
}
}
public static ResponsePacket DeserializeResponse(String s)
{
try
{
JAXBContext jc = JAXBContext.newInstance("wsspsample.xom.response");
Unmarshaller um = jc.createUnmarshaller();
StreamSource source = new StreamSource(new StringReader(s));
return (ResponsePacket)um.unmarshal(source);
}
catch (JAXBException ex)
{
return new ResponsePacket();
}
}
从这里开始,我们要来创建QueryPacket和所需的类,设置相关的值并调用服务。
// Create a Search Query Packet Object
QueryPacket qp = new QueryPacket();
QueryType qt = new QueryType();
ContextType ct = new ContextType();
QueryTextType ctt = new QueryTextType();
ct.setQueryText(ctt);
qt.setContext(ct);
qp.setQuery(qt);
// Set search values
ctt.setValue("sharepoint");
ctt.setType("STRING"); //This is the default - not necessary
ctt.setLanguage("en-us"); //This is the default - not necessary
// Call the web service query
String sResponse = qsp.query(SerializeQuery(qp));
// Deserialize the response
ResponsePacket resp = DeserializeResponse(sResponse);
构建并运行应用程序
我们已经拥有了生成的Web Services代理和查询请求和响应的对象模型,已经使用它们的应用程序,现在我们只需要简单地动下手指头,即可对其进行构建、运行和测试。你需要一个配置好的SharePoint服务器,并在Main.java中设置正确的端点地址,以及能正确测试SharePoint服务器的登录凭据。
Using Data From SharePoint 2003 Lists
A buddy of mine asked me to help him figure out how to get the data from a SharePoint list for a project he’s working on. I thought rather than showing him one way, I’d show him three and let him decide which method is best for his application. Now I think I’ll share this with the rest of the world in case anybody else is doing the same sort of thing.
SharePoint 2003 has several web services that you can use to access information stored in a SharePoint site, all of which are located in the _vti_bin directory of a server running SharePoint 2003. Here’s a quick list of the services available (taken from Using Microsoft Windows SharePoint Services with the Microsoft Office System)
Service | Web Reference URL | Description |
Administration | http://server_name:5966/_vti_adm/Admin.asmx | Administrative methods for managing a deployment of Microsoft Windows SharePoint Services, such as for creating or deleting site collections. |
Alerts | http://server_name/_vti_bin/Alerts.asmx | Methods for working with alerts for list items in a SharePoint site. |
Data Retrieval Service | http://server_name/_vti_bin/DspSts.asmx | Methods for retrieving schemas and data |
Document Workspace | http://server_name/_vti_bin/DWS.asmx | Methods for managing Document Workspace sites and the data they contain |
Forms | http://server_name/_vti_bin/Forms.asmx | Methods for returning forms used in the user interface when working with the contents of a list |
Imaging | http://server_name/_vti_bin/Imaging.asmx | Methods that enable you to create and manage picture libraries |
Lists | http://server_name/_vti_bin/Lists.asmx | Methods for working with lists and list data |
Meetings | http://server_name/_vti_bin/Meetings.asmx | Methods that enable you to create and manage Meeting Workspace sites |
Permissions | http://server_name/_vti_bin/Permissions.asmx | Methods for working with Windows SharePoint Services security |
Site Data | http://server_name/_vti_bin/SiteData.asmx | Methods used by search services to extract and crawl data from SharePoint sites. |
Sites | http://server_name/_vti_bin/Sites.asmx | Method for returning information about the collection of site templates on the virtual server. |
Users and Groups | http://server_name/_vti_bin/UserGroup.asmx | Methods for working with users, site groups, and cross-site groups |
Versions | http://server_name/_vti_bin/versions.asmx | Methods for working with file versions |
Views | http://server_name/_vti_bin/Views.asmx | Methods for working with views of lists |
Web Part Pages | http://server_name/_vti_bin/WebPartPages.asmx | Methods to send information to and retrieve information from XML Web services. |
Webs | http://server_name/_vti_bin/Webs.asmx | Methods for working with sites and subsites |
To retrieve information from a List we’ll use the Lists.asmx web service and the GetListItems method in particular. To do this, add a web reference to your application setting the URL to the location of the Lists web service. The screen should look something like this:
After adding the web reference to your application you can use the following code to retrieve the list data from the web service as an XmlNode.
Private Function GetListNode() As XmlNode
Dim RCPILists As New RCPILists.Lists
RCPILists.Credentials = New System.Net.NetworkCredential("PaulBallard", _
"password")
Dim node As XmlNode = RCPILists.GetListItems("Vacation Calendar", _
String.Empty, Nothing, Nothing, String.Empty, Nothing)
Return node
End Function
Let’s take a look at what the XML returned looks like when querying a list of events.
<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="24">
<z:row ows_fRecurrence="0" ows_Attachments="0"ows_LinkTitle="Tulsa .NET User Group Meeting"
ows_EventDate="2004-09-27 18:00:00" ows_EndDate="2004-09-27 21:00:00" ows_Title="Tulsa .NET User Group Meeting"
ows_ID="2" ows_owshiddenversion="1" />
<z:row ows_fRecurrence="0" ows_Attachments="0"ows_LinkTitle="INETA Board Meeting in Vegas"
ows_EventDate="2004-10-01 16:00:00" ows_EndDate="2004-10-03 16:00:00" ows_Title="INETA Board Meeting in Vegas"
ows_ID="1" ows_owshiddenversion="2" />
<z:row ows_fRecurrence="0" ows_Attachments="0"ows_LinkTitle="Dallas .NET User Group" ows_EventDate="2004-10-14 18:00:00"
ows_EndDate="2004-10-14 21:00:00" ows_Title="Dallas .NET User Group" ows_ID="5" ows_owshiddenversion="2" />
<z:row ows_fRecurrence="0" ows_Attachments="0"ows_LinkTitle="New Orleans .NET User Group Weekend"
ows_EventDate="2004-10-16 09:00:00" ows_EndDate="2004-10-18 21:00:00" ows_Title="New Orleans .NET User Group Weekend"
ows_ID="3" ows_owshiddenversion="2" />
<z:row ows_fRecurrence="0" ows_Attachments="0"ows_LinkTitle="OOPSLA" ows_EventDate="2004-10-23 00:00:00"
ows_EndDate="2004-10-28 00:00:00" ows_Title="OOPSLA"ows_ID="4" ows_owshiddenversion="2" />
<z:row ows_fRecurrence="0" ows_Attachments="0"ows_LinkTitle="Oklahoma City .NET User Group"
ows_EventDate="2004-11-01 00:00:00" ows_Title="Oklahoma City .NET User Group" ows_ID="6"
ows_owshiddenversion="1" />
<z:row ows_fRecurrence="0" ows_Attachments="0"ows_LinkTitle="DevConnections" ows_EventDate="2004-11-07 00:00:00"
ows_EndDate="2004-11-10 23:00:00"ows_Title="DevConnections" ows_ID="11" ows_owshiddenversion="1" />
<z:row ows_fRecurrence="0" ows_Attachments="0"ows_LinkTitle="Thanksgiving" ows_EventDate="2004-11-25 00:00:00"
ows_Title="Thanksgiving" ows_ID="7"ows_owshiddenversion="1" />
<z:row ows_fRecurrence="0" ows_Attachments="0"ows_LinkTitle="CES - Las Vegas" ows_EventDate="2005-01-06 00:00:00"
ows_EndDate="2005-01-07 23:00:00" ows_Title="CES - Las Vegas" ows_ID="12" ows_owshiddenversion="2" />
<z:row ows_fRecurrence="0" ows_Attachments="0"ows_LinkTitle="Devscovery Austin" ows_EventDate="2005-01-11 00:00:00"
ows_EndDate="2005-01-13 23:00:00" ows_Title="Devscovery Austin" ows_ID="14" ows_owshiddenversion="1" />
<z:row ows_fRecurrence="0" ows_Attachments="0"ows_LinkTitle="TechEd 2005 in Orlando" ows_EventDate="2005-06-03 00:00:00"
ows_EndDate="2005-06-11 00:00:00"ows_Description="Remember to have new liver standing by"
ows_Title="TechEd 2005 in Orlando" ows_ID="24"ows_owshiddenversion="1" />
</rs:data>
</listitems>
Many of you may recognize this XML format as that of an ADO Recordset with the columns of the records prefixed with “ows_’. While a relatively simple format, it’s not conducive to working with the data in .NET. So then, here are the three examples of working with this data to fill a Windows Forms ListBox:
1. Convert it to a Dataset (Easiest)
While the format of the ADO Recordset doesn’t match that of an ADO.NET Dataset, the Dataset can read the data in as XML and infer the layout of the tables. To read the XML data into a Dataset you need to create an XmlTextReader that wraps the XmlNode returned by the web service and then call the Dataset’s ReadXml() method passing in the XmlTextReader. Here is what that code might look like:
Private Sub btnGetDS_Click(ByVal sender As System.Object, ByVal e AsSystem.EventArgs) Handles btnGetDS.Click
Dim node As XmlNode = GetListNode()
Dim xr As New XmlTextReader(node.OuterXml, XmlNodeType.Element, Nothing)
Dim ds As New DataSet
ds.ReadXml(xr)
ListBox1.DataSource = ds.Tables("row")
ListBox1.DisplayMember = "ows_Title"
End Sub
When you read this XML into the dataset, there will be two tables created. The first one, at index 0, will be the “data” table and will contain one record with the ItemCount in it. The second table will be the “row” table and will contain the data from the list with a reference to the parent “data” record. Also remember that columns in the table will be prefixed with “ows_”.
2. Access Data Using XPath Queries (Easy)
You can use XPath to locate a single element or a group of elements from the node retrieved using the XmlNode.SelectNodes and XmlNode.SelectSingleNode methods. To search for a single element in the list the code would look something like this:
xpq = "//*[@ows_Title='Thanksgiving']"
Dim node2 As XmlNode = node.SelectSingleNode(xpq)
However, to retrieve all of the rows returned using XPath, we can’t simply use “//data/rows” because of the namespace prefixes. You could create an XPathNavigator loading up the proper namespaces but that struck me as a lot of work. So instead I would use this code to issue an XPath query that would return all rows.
Dim xpq As String = "//*[local-name() = 'data' and namespace-uri() = 'urn:schemas-microsoft-com:rowset']/*[local-name() = 'row' and namespace-uri() = '#RowsetSchema']"
Dim nodes As XmlNodeList = node.SelectNodes(xpq)
For idx As Int32 = 0 To nodes.Count - 1
ListBox1.Items.Add(nodes(idx).Attributes("ows_Title").Value)
Next
3. XmlSerialization (Not-So Easy)
It’s often preferable to work with data in terms of objects with properties. Datasets and XML nodes are relatively large objects and take time to build where as custom business objects contain only the code you decide is there. The key in using XML Serialization for list items are the Xml Attributes defined in the System.Xml.Serialization namespace.
To start we create the generic shell classes that every list will use. These classes represent the “<listitems>” element and “<rs:data>” elements. To specify how the XmlSerializer should deserialize the XML from the Web Service into one of these objects we will apply the XmlElementAttribute to each class specifying the ElementName and Namespace properties. Here is what the code for that looks like:
#Region "Generic List Classes, should not need to be changed"
<XmlRoot(ElementName:="listitems",Namespace:="http://schemas.microsoft.com/sharepoint/soap/")> _
Public Class ListResults
<XmlElement(ElementName:="data", Namespace:="urn:schemas-microsoft-com:rowset")> _
Public Data As ListData
End Class
Public Class ListData
Public Sub New()
End Sub
<XmlAttributeAttribute("ItemCount")> _
Public ItemCount As Int32
<XmlElement(ElementName:="row", Namespace:="#RowsetSchema")> _
Public Items As ListItem()
End Class
#End Region
Next we have to specify the ListItem class that defines the columns in the list with the properties and attributes that we want to use within our application. Any property in the XML that doesn’t have a corresponding property in the class is ignored. In specifying this class we can also remap the column names to remove the “ows_” prefix. Again, we’ll make heavy use of the XmlAttributeAttribute. Here is the code for a record of an Events list in SharePoint 2003.
'Customize this class for your list.
Public Class ListItem
Private m_LinkTitle As String
Private m_EventDate As String
Private m_EventEndDate As String
Private m_Recurrence As Int32
Private m_ID As Int32
Private m_Title As String
Public Sub New()
End Sub
<XmlAttributeAttribute("ows_LinkTitle")> _
Public Property LinkTitle() As String
Get
Return m_LinkTitle
End Get
Set(ByVal Value As String)
m_LinkTitle = Value
End Set
End Property
<XmlAttributeAttribute("ows_EventDate")> _
Public Property EventDate() As String
Get
Return m_EventDate
End Get
Set(ByVal Value As String)
m_EventDate = Value
End Set
End Property
<XmlAttributeAttribute("ows_EndDate")> _
Public Property EventEndDate() As String
Get
Return m_EventEndDate
End Get
Set(ByVal Value As String)
m_EventEndDate = Value
End Set
End Property
<XmlAttributeAttribute("ows_fRecurrence")> _
Public Property Recurrence() As Int32
Get
Return m_Recurrence
End Get
Set(ByVal Value As Int32)
m_Recurrence = Value
End Set
End Property
<XmlAttributeAttribute("ows_Title")> _
Public Property Title() As String
Get
Return m_Title
End Get
Set(ByVal Value As String)
m_Title = Value
End Set
End Property
<XmlAttributeAttribute("ID")> _
Public Property ID() As Int32
Get
Return m_ID
End Get
Set(ByVal Value As Int32)
m_ID = Value
End Set
End Property
End Class
Once the class is defined, we can use the XmlSerializer to convert the XML from the web service into an instance of this object. Here is the code to does that and then binds the data to a ListBox.
Private Sub btnGetObj_Click(ByVal sender As System.Object, ByVal eAs System.EventArgs) Handles btnGetObj.Click
Dim node As XmlNode = GetListNode()
Dim xr As New XmlTextReader(node.OuterXml, XmlNodeType.Element, Nothing)
Dim ser As New XmlSerializer(GetType(ListResults))
Dim lResults As ListResults = CType(ser.Deserialize(xr), ListResults)
ListBox1.DataSource = lResults.Data.Items
ListBox1.DisplayMember = "Title"
End Sub
Which to Choose
Now of course the question is which one should you use? That I leave up to you fair reader, but here are some things to consider about each approach.
Datasets
Datasets are large objects and take up quite a bit of memory, but can be very useful if you are planning to bind the data directly to a UI control or if you want the user to have the ability to add/edit items in the list. You can mitigate the memory overhead by caching the list results in a multi-user environment.
XML
The XML produced by the web service is difficult to work with and manipulate as compared to a Dataset or business object. XmlNode objects also take up a lot of memory. However, if you are looking for a single item in the list, this is a very expedient way to get it as it requires no other objects be created or managed.
Xml Serialization
XML Serialization is useful for occasions when you need to work with the data returned as an object and apply business rules or logic to the object while it’s being used. Keep in mind that the data returned in the recordset format cannot be automatically converted into .NET types by the XmlSerializer. For example, the date format used in the XML will not parse into a DateTime property. Therefore you may need to create a wrapper around these classes to provide better type safety and usefulness.