SharePoint Foundation 2010 マネージ クライアント オブジェクト モデルで Open XML SDK 2.0 を使用する

概要: Microsoft SharePoint Foundation 2010 マネージ クライアント オブジェクト モデルを使用すると、SharePoint Foundation 2010 を実行するサーバーにコードをインストールしなくてもクライアントから SharePoint コンテンツにアクセスできる、Microsoft .NET Framework ベースのアプリケーションを記述できます。Open XML SDK 2.0 for Microsoft Office を使用すると、Open XML ドキュメント (Microsoft 2007 Office system および Microsoft Office 2010 の既定のドキュメント形式) を作成、変更、およびクエリするアプリケーションを記述できます。これらの 2 つのテクノロジを組み合わせることで、ドキュメント ライブラリに格納された Open XML ドキュメントを操作するクライアント側のアプリケーションを記述できます。

最終更新日: 2011年11月4日

適用対象: Business Connectivity Services | Office 2010 | Open XML | SharePoint Designer 2010 | SharePoint Foundation 2010 | SharePoint Online | SharePoint Server 2010 | Visual Studio

この記事の内容
概要
ドキュメント ライブラリからドキュメントを取得する
ドキュメント ライブラリのドキュメントを変更する
ドキュメント ライブラリにドキュメントをアップロードする
メモリ内にドキュメントを作成して、ドキュメント ライブラリに追加する
ライブラリ内のドキュメントをすべて処理する
Wiki ライブラリから Open XML 形式のワープロ ドキュメントを作成する
まとめ
参考資料

提供元:  Eric White、Microsoft Corporation

目次

  • 概要

  • ドキュメント ライブラリからドキュメントを取得する

  • ドキュメント ライブラリのドキュメントを変更する

  • ドキュメント ライブラリにドキュメントをアップロードする

  • メモリ内にドキュメントを作成して、ドキュメント ライブラリに追加する

  • ライブラリ内のドキュメントをすべて処理する

  • Wiki ライブラリから Open XML 形式のワープロ ドキュメントを作成する

  • まとめ

  • 参考資料

概要

Microsoft SharePoint Foundation 2010 マネージ クライアント オブジェクト モデルは、Microsoft .NET Framework ベースの一連のマネージ ライブラリです。これらのマネージ ライブラリを使用すると、クライアント コンピューターから SharePoint サイト内の多くの共通オブジェクトを操作するコードを記述できます。クライアント側で実行されるプログラムでは、リストの追加と削除、リスト項目の追加、更新、および削除、ドキュメント ライブラリ内のドキュメントの変更、サイトの作成、アイテムのアクセス許可の管理、およびページへの Web パーツの追加と削除を実行できます。

2007 Microsoft Office リリースおよび 2010 Office リリースのドキュメント形式である Open XML ファイル形式は、ワープロ、スプレッドシート、およびプレゼンテーション ドキュメントの構造を定義する ISO/IEC 標準規格 (IS29500) です。Open XML 形式ファイルは、Open Packaging Conventions 仕様 (IS29500 の Part 2) に従って格納されます。これらのファイルは基本的には ZIP ファイルで、内部に XML パーツが含まれます。Open XML SDK 2.0 for Microsoft Office は, .NET Framework ベースのマネージ ライブラリで、Open XML ドキュメントを作成、変更、またはクエリするプログラムを簡単に記述できます。この資料の最後に、いくつかの参考資料を掲載しています。Open XML SDK 2.0 を初めて使用する場合は、これらの資料が役立ちます。

これらの 2 つのテクノロジを組み合わせると、次のような使い方ができます。

  • 任意の場所から取得したコンテンツを利用してワープロ ドキュメントやプレゼンテーションを作成し、そのドキュメントを SharePoint サイトのドキュメント ライブラリにアップロードする、クライアント側のアプリケーションを記述できます。

  • ドキュメント ライブラリ内のすべてのドキュメントをクエリして各ドキュメントから情報を取得するアプリケーションを記述できます。

  • ドキュメント ライブラリ内のすべてのドキュメントを処理して、各ドキュメントに同じ変更を個別に加えるアプリケーションを記述できます。たとえば、便利な使い方として、コメントと個人情報をすべて削除し、ドキュメント ライブラリ内の各ドキュメントの変更履歴をすべて受け付けることができます。

  • Wiki サイトのコンテンツを取得し、そのコンテンツを基にして Open XML ドキュメントを作成できます。

この資料は、「SharePoint Foundation 2010 のマネージ クライアント オブジェクト モデルの使用」の内容に基づいて作成されています。SharePoint Foundation 2010 マネージ クライアント オブジェクト モデルの機能の詳細については、そちらの資料を参照してください。

ドキュメント ライブラリからドキュメントを取得する

SharePoint Foundation 2010 マネージ クライアント オブジェクト モデルと Open XML 形式を使用する場合、基本的には 2 つの操作を行います。1 つはドキュメントを取得すること、もう 1 つはドキュメントを保存することです。以下の例では、ドキュメント ライブラリからドキュメントを取得して、第 1 段落のテキストを出力します。

注意

この資料では、Microsoft Windows コンソール アプリケーションをサンプル コードに使用していますが、別の種類のアプリケーションを使用しても同じ操作を実行できます。

この例を作成するには、4 つのアセンブリへの参照を追加する必要があります。SharePoint Foundation 2010 マネージ クライアント オブジェクト モデルへのアクセスに必要なアセンブリとして、Microsoft.SharePoint.Client.dll と Microsoft.SharePoint.Client.Runtime.dll の 2 つ、 また、Open XML SDK 2.0 を使用するために必要なアセンブリとして、DocumentFormat.OpenXml.dll と WindowsBase.dll の 2 つがあります。 SharePoint Foundation 2010 マネージ クライアント オブジェクト モデルを使用するコンソール アプリケーションの作成方法の詳細については、「SharePoint Foundation 2010 のマネージ クライアント オブジェクト モデルの使用」を参照してください。

また、この資料に示す例を作成するには、Open XML SDK 2.0 をダウンロードしてインストールする必要もあります。Open XML SDK 2.0 アプリケーションの作成方法の詳細については、「Open XML SDK 2.0 for Microsoft Office へようこそ」を参照してください。Open XML SDK 2.0 のダウンロードする場合は、「Open XML SDK 2.0」を参照してください。

using System;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using Microsoft.SharePoint.Client;
// The following using directive resolves the ambiguity
// between the System.IO.File class and Microsoft.SharePoint.Client.File
// class.
using ClientOM = Microsoft.SharePoint.Client;

class Program
{
    static private void CopyStream(Stream source, Stream destination)
    {
        byte[] buffer = new byte[32768];
        int bytesRead;
        do
        {
            bytesRead = source.Read(buffer, 0, buffer.Length);
            destination.Write(buffer, 0, bytesRead);
        } while (bytesRead != 0);
    }

    static void Main(string[] args)
    {
        ClientContext clientContext =
            new ClientContext("http://intranet.contoso.com");
        List sharedDocumentsList = clientContext.Web.Lists
            .GetByTitle("Shared Documents");
        CamlQuery camlQuery = new CamlQuery();
        camlQuery.ViewXml =
            @"<View>
                <Query>
                  <Where>
                    <Eq>
                      <FieldRef Name='FileLeafRef'/>
                      <Value Type='Text'>Test.docx</Value>
                    </Eq>
                  </Where>
                  <RowLimit>1</RowLimit>
                </Query>
              </View>";
        ListItemCollection listItems = sharedDocumentsList.GetItems(camlQuery);
        clientContext.Load(sharedDocumentsList);
        clientContext.Load(listItems);
        clientContext.ExecuteQuery();
        if (listItems.Count == 1)
        {
            ClientOM.ListItem item = listItems[0];
            Console.WriteLine("FileLeafRef: {0}", item["FileLeafRef"]);
            Console.WriteLine("FileDirRef: {0}", item["FileDirRef"]);
            Console.WriteLine("FileRef: {0}", item["FileRef"]);
            Console.WriteLine("File Type: {0}", item["File_x0020_Type"]);
            FileInformation fileInformation =
                ClientOM.File.OpenBinaryDirect(clientContext, (string)item["FileRef"]);
            using (MemoryStream memoryStream = new MemoryStream())
            {
                CopyStream(fileInformation.Stream, memoryStream);
                using (WordprocessingDocument doc =
                    WordprocessingDocument.Open(memoryStream, false))
                {
                    var firstParagraph = doc
                        .MainDocumentPart
                        .Document
                        .Body
                        .Elements<Paragraph>()
                        .FirstOrDefault();
                    if (firstParagraph != null)
                    {
                        string text = firstParagraph.InnerText;
                        Console.WriteLine(text);
                    }
                    else
                        Console.WriteLine("Document doesn't contain any paragraphs.");
                }
            }
        }
        else
            Console.WriteLine("Document not found.");
    }
}

この例では、次のような出力が得られます。

FileLeafRef: Test.docx
FileDirRef: /Shared Documents
FileRef: /Shared Documents/Test.docx
File Type: docx
This is the text of the first paragraph.

この例では、フィールドを出力しています。通常は、ドキュメント ライブラリ内のドキュメントを操作するときに使用するフィールドです。

  • FileLeafRef フィールドには、ファイル名が含まれます。

  • FileDirRef フィールドには、ドキュメント ライブラリ名が含まれます。フォルダー内にファイルがある場合は、FileDirRef フィールドにフォルダー名も含まれます。

  • FileRef フィールドには、完全な名前が含まれます。つまり、ドキュメント ライブラリ名、フォルダー名、ファイル名などが含まれます。

CAML クエリは、FileLeafRef フィールドの値が Test.docx に設定されたアイテムをドキュメント ライブラリから探し、 View 要素の RowLimit 要素を 1 に設定します。

camlQuery.ViewXml =
    @"<View>
        <Query>
          <Where>
            <Eq>
              <FieldRef Name='FileLeafRef'/>
              <Value Type='Text'>Test.docx</Value>
            </Eq>
          </Where>
          <RowLimit>1</RowLimit>
        </Query>
      </View>";

この例では、RowLimit 要素を 1 に設定しているため、含まれる項目数が 0 または 1 の ListItemCollection オブジェクトを返します。したがって、listItems.Count == 1 を使用することによって、ドキュメントが見つかったかどうかを確認できます。

また、ファイルを取得するために、OpenBinaryDirect() メソッドを使用して FileInformation 型のオブジェクトを返しています。

FileInformation fileInformation =
    ClientOM.File.OpenBinaryDirect(clientContext, (string)item["FileRef"]);

FileInformation オブジェクトにはストリームへの参照が含まれています。これは、アプリケーションへのファイルのストリームに使用するものです。ただし、このストリームは可変サイズのストリームではありません。WordprocessingDocument クラスの Open() メソッドは、ストリームを引数に取りますが、その場合、ストリームは可変サイズである必要があります。そのため、MemoryStream (英語) オブジェクトを作成した後、Stream プロパティが返すストリームから MemoryStream オブジェクトにコピーする必要があります。Stream プロパティが返すストリームから WordprocessingDocument オブジェクトを開こうとすると、Open() メソッドは、"ストリームで FileMode または FileAccess 値が無効なため、パッケージを開くことができません。" というメッセージと共に、例外 System.IO.IOException (英語) をスローします。

Stream プロパティにアクセスする前に ExecuteQuery() メソッドを呼び出す必要はないことに気付いた方もいらっしゃるでしょう。これは、OpenBinaryDirect() メソッドは、クライアント オブジェクト モデルの通常の XML/JSON ネットワーク通信に参加しないためです。「Using the SharePoint Foundation 2010 Managed Client Object Model」では、クライアント オブジェクト モデルのクライアント部分が要求を XML としてバンドルする方法、XML がサーバーに送信されて (これによって要求を実行する)、応答を JSON で送信する方法について説明しています。XML と JSON を使用してファイルをアップロードすると、クライアント オブジェクト モデルは、バイナリ Open XML 形式ドキュメントを Base-64 エンコードに変換します。ただし、このエンコードは、バイナリ データを直接送信する場合と比べて低効率です。したがって、できれば、バイナリ データを直接送信することをお勧めします。こうすれば、アプリケーションの効率は高くなり、ネットワーク トラフィックは低下します。

MemoryStream オブジェクトを作成したら (パラメーターを取らないコンストラクターを使用して作成すると、可変サイズのオブジェクトになります)、Open() メソッドでドキュメントを開いて通常どおりに処理します。

注意に関するメモ注意

MemoryStream クラスには、バイト配列を取るコンストラクター (英語)があります (このバイト配列は、後でストリームのバッキング ストアとして使用されます)。ただし、このコンストラクターが返すメモリ ストリームは可変サイズでないため、このコンストラクターを使用しても、Open XML API で使用するメモリ ストリームは作成できません。

ドキュメント ライブラリのドキュメントを変更する

前の例を修正し、ドキュメントを変更して、変更後のドキュメントをドキュメント ライブラリに返すようにします。

using System;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using Microsoft.SharePoint.Client;
using ClientOM = Microsoft.SharePoint.Client;

class Program
{
    static private void CopyStream(Stream source, Stream destination)
    {
        byte[] buffer = new byte[32768];
        int bytesRead;
        do
        {
            bytesRead = source.Read(buffer, 0, buffer.Length);
            destination.Write(buffer, 0, bytesRead);
        } while (bytesRead != 0);
    }

    static void Main(string[] args)
    {
        ClientContext clientContext =
            new ClientContext("http://intranet.contoso.com");
        List sharedDocumentsList = clientContext.Web.Lists
            .GetByTitle("Shared Documents");
        CamlQuery camlQuery = new CamlQuery();
        camlQuery.ViewXml =
            @"<View>
                <Query>
                  <Where>
                    <Eq>
                      <FieldRef Name='FileLeafRef'/>
                      <Value Type='Text'>Test.docx</Value>
                    </Eq>
                  </Where>
                  <RowLimit>1</RowLimit>
                </Query>
              </View>";
        ListItemCollection listItems =
            sharedDocumentsList.GetItems(camlQuery);
        clientContext.Load(sharedDocumentsList);
        clientContext.Load(listItems);
        clientContext.ExecuteQuery();
        if (listItems.Count == 1)
        {
            ClientOM.ListItem item = listItems[0];
            Console.WriteLine("FileLeafRef: {0}", item["FileLeafRef"]);
            Console.WriteLine("FileDirRef: {0}", item["FileDirRef"]);
            Console.WriteLine("FileRef: {0}", item["FileRef"]);
            Console.WriteLine("File Type: {0}", item["File_x0020_Type"]);
            FileInformation fileInformation =
                ClientOM.File.OpenBinaryDirect(clientContext,
                (string)item["FileRef"]);
            using (MemoryStream memoryStream = new MemoryStream())
            {
                CopyStream(fileInformation.Stream, memoryStream);
                using (WordprocessingDocument doc =
                    WordprocessingDocument.Open(memoryStream, true))
                {
                    // Insert a new paragraph at the beginning of the
                    //document.
                    doc.MainDocumentPart.Document.Body.InsertAt(new Paragraph(new Run(new Text("Newly inserted paragraph."))), 0);
                }
                // Seek to beginning before writing to the SharePoint server.
                memoryStream.Seek(0, SeekOrigin.Begin);
                // SaveBinaryDirect replaces the document on the SharePoint server.
                ClientOM.File.SaveBinaryDirect(clientContext,(string)item["FileRef"], memoryStream, true);
            }
        }
        else
            Console.WriteLine("Document not found.");
    }
}

この例では、Open() メソッドに True を渡してドキュメントを変更できるようにしています。Open() メソッドでドキュメントを開いた後は、ドキュメントを通常どおり変更できます。

Open() メソッドを呼び出してドキュメントを開くステートメントで作成されたスコープから出ると、MemoryStream オブジェクトには変更後のドキュメントが含まれています。これをサーバーにストリームするには、ストリームの先頭を探す必要があります。先頭が見つかったら、SaveBinaryDirect() メソッドを呼び出して、変更後のドキュメントをドキュメント ライブラリに返します。

ドキュメント ライブラリにドキュメントをアップロードする

次は、既存のドキュメントをドキュメント ライブラリにアップロードします。ドキュメントをアップロードするには、SaveBinaryDirect() メソッドを呼び出して、サーバーの相対 URL とストリームを渡します。次の例では、新しいドキュメントを SharePoint サイトの共有ドキュメント フォルダーにアップグレードします。

using System;
using System.IO;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using Microsoft.SharePoint.Client;
// The following directive avoids ambiguity between the
// System.IO.File class and Microsoft.SharePoint.Client.File class.
using ClientOM = Microsoft.SharePoint.Client;

class Program
{
    static void Main(string[] args)
    {
        ClientContext clientContext =
            new ClientContext("http://intranet.contoso.com");
        using (FileStream fileStream =
            new FileStream("NewDocument.docx", FileMode.Open))
            ClientOM.File.SaveBinaryDirect(clientContext,
                "/Shared Documents/NewDocument.docx", fileStream, true);
    }
}

メモリ内にドキュメントを作成して、ドキュメント ライブラリに追加する

Open XML 形式 API を使用してドキュメントを作成する方法には、一般的に次の 2 つの方法があります。

  • 既存のドキュメントをテンプレートとして使用します。そのドキュメントを開き、必要な変更を加えたら、希望の場所に保存します。

  • ドキュメントを作成します。通常は Open XML SDK 2.0 for Microsoft Office に付属する DocumentReflector ツールを使用します。このツールを使用すると、Open XML 形式ドキュメントを作成する Microsoft Visual C# コードを生成できます。

どちらの方法でも、ドキュメントを MemoryStream オブジェクト内に作成できます。以下の例では、ドキュメントをバイト配列に読み取り、引数を取らない MemoryStream コンストラクターを使用して可変サイズのメモリ ストリームを作成します。さらに、バイト配列を MemoryStream に書き込み、メモリ内のドキュメントを変更し、その変更後のドキュメントを SharePoint サイトの共有ドキュメント ライブラリにアップロードします。

この例を実行するには、Template.docx という名前のワープロ ドキュメントを作成して、必要なコンテンツ (またはコンテンツなし) で初期化したら、この例を実行する bin ディレクトリに配置します。この例では、ドキュメントをドキュメント ライブラリにアップロードする前に、ドキュメントの先頭に新しい段落を挿入します。

using System;
using System.IO;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using Microsoft.SharePoint.Client;
using ClientOM = Microsoft.SharePoint.Client;

class Program
{
    static void Main(string[] args)
    {
        ClientContext clientContext =
            new ClientContext("http://intranet.contoso.com");
        byte[] byteArray = System.IO.File.ReadAllBytes("Template.docx");
        using (MemoryStream memoryStream = new MemoryStream())
        {
            memoryStream.Write(byteArray, 0, (int)byteArray.Length);
            using (WordprocessingDocument doc =
                WordprocessingDocument.Open(memoryStream, true))
            {
                // Insert a new paragraph at the beginning of the
                //document.
                doc.MainDocumentPart.Document.Body.InsertAt(
                    new Paragraph(
                        new Run(
                            new Text("Newly inserted paragraph."))), 0);
            }
            string fileUrl = "/Shared Documents/NewDocumentFromTemplate.docx";
            memoryStream.Seek(0, SeekOrigin.Begin);
            ClientOM.File.SaveBinaryDirect(clientContext, fileUrl, memoryStream, true);
        }
    }
}

ライブラリ内のドキュメントをすべて処理する

ドキュメント ライブラリのすべてのドキュメントを反復処理して、各ドキュメントに同じクエリや変更を実行したい場合があります。ドキュメント ライブラリには、ドキュメントだけでなく、サブフォルダーも含むフォルダーが含まれている場合があります。以下の例では、View 要素の Scope=’Recursive’ 属性を使用してドキュメント ライブラリ内のドキュメントをすべて処理する方法を示しています。サブフォルダー内のドキュメントも対象に含まれます。

注意に関するメモ注意

ドキュメント数が 2,000 を超える大きなドキュメント ライブラリ内のドキュメントをすべて処理する場合は、ページング アルゴリズムを実装する必要があります。詳細については、「Using the SharePoint Foundation 2010 Managed Client Object Model」を参照してください。このような処理を行う対話操作アプリケーションを作成する場合は、非同期処理を使用すると有効です。詳細については、「非同期処理」を参照してください。

using System;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using Microsoft.SharePoint.Client;
using ClientOM = Microsoft.SharePoint.Client;

class Program
{
    static private void CopyStream(Stream source, Stream destination)
    {
        byte[] buffer = new byte[32768];
        int bytesRead;
        do
        {
            bytesRead = source.Read(buffer, 0, buffer.Length);
            destination.Write(buffer, 0, bytesRead);
        } while (bytesRead != 0);
    }

    static void Main(string[] args)
    {
        ClientContext clientContext =
            new ClientContext("http://intranet.contoso.com");
        List sharedDocumentsList = clientContext.Web.Lists
            .GetByTitle("Shared Documents");
        CamlQuery camlQuery = new CamlQuery();
        camlQuery.ViewXml =
            @"<View Scope='Recursive'>
                <Query>
                  <Where>
                    <Eq>
                      <FieldRef Name='File_x0020_Type'/><Value Type='Text'>docx</Value>
                    </Eq>
                  </Where>
                </Query>
              </View>";
        ListItemCollection listItems =
            sharedDocumentsList.GetItems(camlQuery);
        clientContext.Load(listItems);
        clientContext.ExecuteQuery();
        foreach (var item in listItems)
        {
            Console.WriteLine("FileLeafRef: {0}", item["FileLeafRef"]);
            Console.WriteLine("FileDirRef: {0}", item["FileDirRef"]);
            Console.WriteLine("FileRef: {0}", item["FileRef"]);
            FileInformation fileInformation =
                ClientOM.File.OpenBinaryDirect(clientContext,
                (string)item["FileRef"]);
            using (MemoryStream memoryStream = new MemoryStream())
            {
                CopyStream(fileInformation.Stream, memoryStream);
                using (WordprocessingDocument doc =
                    WordprocessingDocument.Open(memoryStream, true))
                {
                    var firstParagraph = doc.MainDocumentPart.Document
                        .Body.Elements<Paragraph>().FirstOrDefault();
                    if (firstParagraph != null)
                    {
                        string text = firstParagraph.InnerText;
                        Console.WriteLine("  First paragraph text: {0}",
                            text);
                    }
                    else
                        Console.WriteLine(
                            "Document doesn't contain any paragraphs.");
                }
            }
            Console.WriteLine();
        }
    }
}

ドキュメント ライブラリまたはフォルダー内の Open XML 形式ドキュメントをすべて処理する場合は、CAML クエリでフィルターを適用して希望の種類のドキュメント (この例では "docx") を選択すると便利です。この例の場合、Collaborative Application Markup Language (CAML) クエリのコード内の強調している部分がフィルターを適用している箇所です。

Open XML プロジェクトに対応した PowerTools は、ワープロ ドキュメントの変更履歴をすべて受け付けるライブラリを備えています。この例に簡単な変更を加えるだけで、ドキュメント ライブラリ内のすべてのドキュメントを反復処理して、各ドキュメントの変更を受け付けるプログラムを作成できます。RevisionAccepter クラスを探す場合は、「PowerTools for Open XML (英語)」の「Downloads」セクションを参照してください。

Wiki ライブラリから Open XML 形式のワープロ ドキュメントを作成する

クライアント オブジェクト モデルで Open XML API を使用すると、斬新なソリューションを容易に作成できるようになります。たとえば、ある Wiki ユーザーが、Wiki サイトにアクセスできないユーザーにドキュメントを配布するつもりであるとします。企業ネットワークにはアクセスできないが、Wiki の情報を必要とする顧客やベンダーもこのようなユーザーに該当することがあります。SharePoint Wiki ライブラリは Open XML 形式ドキュメントに変換して、Microsoft Office Word 2007 または Microsoft Word 2010 を所有するユーザーに送信することができます。ここでは、この変換をクライアント オブジェクト モデルを使用して行う方法について説明します。

この処理には、Open XML 形式の altChunk 機能を使用します。WordprocessingML には、altChunk 要素を通じて外部コンテンツをインポートする機能があります。altChunk を使用するには次のようにします。

  • パッケージ内に追加のパーツを作成します。このパーツには数種類のコンテンツ タイプを含めることができます。たとえば、Open XML 形式ドキュメント全体、HTML、プレーン テキストを含めることができます。この例では、HTML のインポート機能を使用します。

  • このパーツにインポートするコンテンツを格納します。

  • このパーツを作成するときに、主要なドキュメント パーツから新しい代替形式のパーツへの関係を作成します。

  • 代替形式のコンテンツをインポートする場所に w:altChunk 要素を追加します。w:altChunk 要素の r:id 属性はインポートするコンテンツを識別します。w:altChunk 要素はブロックレベルのコンテンツです。つまり、この要素は、ドキュメント内の段落 (w:p) 要素を挿入できる位置であればどこにでも挿入できます。

    この例では、Wiki 内の各トピックの HTML を取得し、各ページを個々のパーツに挿入し、w:altChunk 要素を使用してドキュメント内に各パーツをインポートします。

重要なメモ重要

重要なことを説明します。altChunk は、コンテンツのインポートにのみ使用してください。Word 2007 または Word 2010 でドキュメントを開いて保存する場合、新しく保存されるドキュメントには代替形式のコンテンツも、それを参照する altChunk マークアップも取り込まれません。Word 2007 または Word 2010 では、HTML は、段落要素 (w:p)、実行要素 (w:r)、テキスト要素 (w:t) などの WordprocessingML マークアップに変換されます。

次の例では、各 Wiki ページのコンテンツを HTML として取得します。

using System;
using System.Xml.Linq;
using Microsoft.SharePoint.Client;

class RetrieveListItems
{
    static void Main()
    {
        ClientContext clientContext = new ClientContext("http://intranet.contoso.com");
        List oList = clientContext.Web.Lists.GetByTitle("Eric's Wiki");
        CamlQuery camlQuery = new CamlQuery();
        camlQuery.ViewXml = @"<View/>";
        ListItemCollection listItems = oList.GetItems(camlQuery);
        clientContext.Load(
             listItems,
             items => items.Include(
                 item => item["FileRef"],
                 item => item["WikiField"]));
        clientContext.ExecuteQuery();
        foreach (ListItem oListItem in listItems)
        {
            Console.WriteLine("FileRef: {0}", oListItem["FileRef"]);
            XElement e = XElement.Parse((string)oListItem["WikiField"]);
            Console.WriteLine(e);
            Console.WriteLine("====================================");
        }
    }
}

次に、[M:Microsoft.SharePoint.Client.ClientContext.#Ctor] コンストラクターに渡される URL を SharePoint サイトの URL に変更します。また、GetByTitle() メソッドに渡されるタイトルを SharePoint サイトの Wiki ライブラリのタイトルに変更します。

この例では、LINQ to XML を使用して、Wiki ライブラリから取得した XHTML に書式 (インデント) を設定します。この例を実行すると、次のような表示になります。

FileRef: /Erics Wiki/AnotherPage.aspx
<div class="ExternalClass7EB3EF2C25AB481081BEE2BF8D80B6B8">
  <table id="layoutsTable" style="width:100%">
    <tbody>
      <tr style="vertical-align:top">
        <td style="width:100%">
          <div class="ms-rte-layoutszone-outer" style="width:100%"><div><p>This is some content in another page.</p><p> </p><p><strong>Here is some bold text.</strong></p><p></p></div></div>
        </td>
      </tr>
    </tbody>
  </table>

  <span id="layoutsData" style="display:none">false,false,1</span>
</div>
====================================
FileRef: /Erics Wiki/This is a longer title of a page.aspx
<div class="ExternalClass980D47DB1C51449E9684343F131C5689">
  <table id="layoutsTable" style="width:100%">
    <tbody>
      <tr style="vertical-align:top">
        <td style="width:100%">
          <div class="ms-rte-layoutszone-outer" style="width:100%"><div><p>This page contains some content we will extend in the future.</p><p></p></div></div>
        </td>
      </tr>
    </tbody>
  </table>

  <span id="layoutsData" style="display:none">false,false,1</span>
</div>

ご覧のように、Wiki ライブラリ内の各ページのコンテンツは <div> 要素の中に記述されています。<div> 要素の内側は、Wiki のレイアウトに使用するテーブル要素です。特に興味深いのは、ms-rte-layoutszone-outer という値を持つクラス要素を含むテーブル セル内の <div> 要素です。 代替形式のインポート パーツに挿入する HTML を構築するときは、内側の <div> 要素を HTML ドキュメントの <body> 要素に挿入します。このコード例では、その後、新しく構築された HTML を代替コンテンツ パーツに書き込みます。

Visual Basic に関するメモVisual Basic に関するメモ

この変換では、Wiki リンクはドキュメント内のブックマークやリンクに変換されません。また、Wiki ページに挿入できる文字もありますが、これらは正しく変換されません。ここに示す変換は、クライアント オブジェクト モデルと Open XML 形式 API との使用例を示すものにすぎず、SharePoint Wiki から Open XML 形式ドキュメントへの変換に関するすべての問題を解決することを目的としたものではありません。

次に示すのは、SharePoint Wiki のすべてのページのコンテンツを取得し、それらのコンテンツを基にしてワープロ ドキュメントを作成する例です。

using System;
using System.Linq;
using System.IO;
using System.Xml.Linq;
using Microsoft.SharePoint.Client;
using ClientOM = Microsoft.SharePoint.Client;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;

class RetrieveListItems
{
    static void Main()
    {
        ClientContext clientContext =
            new ClientContext("http://intranet.contoso.com");
        List oList = clientContext.Web.Lists.GetByTitle("Eric's Wiki");
        CamlQuery camlQuery = new CamlQuery();
        camlQuery.ViewXml = @"<View/>";
        ListItemCollection listItems = oList.GetItems(camlQuery);
        clientContext.Load(
             listItems,
             items => items.Include(
                 item => item["FileLeafRef"],
                 item => item["WikiField"]));
        clientContext.ExecuteQuery();

        System.IO.File.Delete("WikiDocument.docx");
        System.IO.File.Copy("EmptyDocument.docx", "WikiDocument.docx");
        int altChunkIdCounter = 1;
        int blockLevelCounter = 0;
        bool first = true;
        using (WordprocessingDocument myDoc =
            WordprocessingDocument.Open("WikiDocument.docx", true))
        {
            MainDocumentPart mainPart = myDoc.MainDocumentPart;
            mainPart.Document.Body.RemoveAllChildren<Paragraph>();
            foreach (ClientOM.ListItem listItem in listItems)
            {
                string wikiPageTitleAspx = (string)listItem["FileLeafRef"];
                string wikiPageTitle = wikiPageTitleAspx.Substring(
                    0, wikiPageTitleAspx.Length - 5);
                if (first)
                {
                    first = false;
                    mainPart.Document.Body.InsertAt(new Paragraph(
                        new ParagraphProperties(
                            new RunProperties(
                                new RunFonts() {
                                    AsciiTheme = ThemeFontValues.MajorHighAnsi,
                                    HighAnsiTheme = ThemeFontValues.MajorHighAnsi },
                                new Bold(),
                                new Color() { Val = "1F497D",
                                    ThemeColor = ThemeColorValues.Text2 },
                                new FontSize() { Val = 28 },
                                new FontSizeComplexScript() { Val = 28 }),
                            new ParagraphStyleId() { Val = "Heading1" }),
                        new Run(
                            new RunProperties(
                                new RunFonts() {
                                    AsciiTheme = ThemeFontValues.MajorHighAnsi,
                                    HighAnsiTheme = ThemeFontValues.MajorHighAnsi },
                                new Bold(),
                                new Color() { Val = "1F497D",
                                    ThemeColor = ThemeColorValues.Text2 },
                                new FontSize() { Val = 28 },
                                new FontSizeComplexScript() { Val = 28 }),
                            new Text(wikiPageTitle))),
                        blockLevelCounter++);
                }
                else
                {
                    mainPart.Document.Body.InsertAt(new Paragraph(
                        new Run(
                            new Break() { Type = BreakValues.Page } )),
                        blockLevelCounter++);
                    mainPart.Document.Body.InsertAt(new Paragraph(
                        new ParagraphProperties(
                            new RunProperties(
                                new RunFonts() {
                                    AsciiTheme = ThemeFontValues.MajorHighAnsi,
                                    HighAnsiTheme = ThemeFontValues.MajorHighAnsi },
                                new Bold(),
                                new Color() { Val = "1F497D",
                                    ThemeColor = ThemeColorValues.Text2 },
                                new FontSize() { Val = 28 },
                                new FontSizeComplexScript() { Val = 28 }),
                            new ParagraphStyleId() { Val = "Heading1" }),
                        new Run(
                            new RunProperties(
                                new RunFonts() {
                                    AsciiTheme = ThemeFontValues.MajorHighAnsi,
                                    HighAnsiTheme = ThemeFontValues.MajorHighAnsi },
                                new Bold(),
                                new Color() { Val = "1F497D",
                                    ThemeColor = ThemeColorValues.Text2 },
                                new FontSize() { Val = 28 },
                                new FontSizeComplexScript() { Val = 28 }),
                            new Text(wikiPageTitle))),
                        blockLevelCounter++);
                }
                XElement wikiField = XElement.Parse((string)listItem["WikiField"]);
                XElement div = wikiField.Descendants("div").Where(e =>
                    (string)e.Attribute("class") == "ms-rte-layoutszone-inner" )
                    .FirstOrDefault();
                string html = String.Format(
                    "<html><head/><body>{0}</body></html>",
                    div != null ? div.ToString() : "");
                string altChunkId = String.Format("AltChunkId{0}",
                    altChunkIdCounter++);
                AlternativeFormatImportPart chunk =
                    mainPart.AddAlternativeFormatImportPart(
                    AlternativeFormatImportPartType.Xhtml, altChunkId);
                using (Stream chunkStream = chunk.GetStream(FileMode.Create,
                    FileAccess.Write))
                using (StreamWriter stringStream = new StreamWriter(chunkStream))
                    stringStream.Write(html);
                AltChunk altChunk = new AltChunk();
                altChunk.Id = altChunkId;
                mainPart.Document.Body.InsertAt(altChunk,
                    blockLevelCounter++);
            }
            mainPart.Document.Save();
        }
    }
}

 

130 行にも満たないコードの中に、多数の機能が記述されています。次に示すのは、SharePoint Wiki 内のページの表示方法です。

図 1. Wiki ライブラリのページ

図 1

 

これと同じページが、Word 2010 には次のように表示されます。

図 2. Open XML 形式のワープロ ドキュメントと同じページ

図 2

 

まとめ

SharePoint Foundation 2010 マネージ クライアント オブジェクト モデルを使用すると、SharePoint Foundation 2010 および SharePoint Server 2010 を操作する強力なクライアント側のアプリケーションを記述できます。Open XML SDK 2.0 を使用すると、Open XML 形式ドキュメントを作成、変更、およびクエリできます。これらを組み合わせることで、情報の共有と情報の共同操作に Open XML ドキュメントを使用するという新しい使い方ができるようになります。

参考資料

この資料では、Open XML SDK 2.0 と SharePoint Foundation 2010 マネージ クライアント オブジェクト モデルという 2 つのテクノロジを組み合わせています。Open XML を初めて使用する場合は、まず、「Open XML Developer Center on MSDN (英語)」をご覧ください。資料、ハウツー ビデオ、さまざまなブログ投稿へのリンクなど、多数のコンテンツが掲載されています。特に、次のリンクからは Open XML SDK 2.0 の使用に関する重要な情報にアクセスできます。

SharePoint Foundation 2010 マネージ クライアント オブジェクト モデルの使用に関して役立つ資料が多数あります。まず、「SharePoint Foundation 2010 のマネージ クライアント オブジェクト モデルの使用」をご覧ください。また、次の資料にもクライアント オブジェクト モデルに関する有益な情報が記載されています。