Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
This example builds on the previous example, Retrieve the paragraphs and their styles. This new example retrieves the text of each paragraph as a string.
To retrieve the text, this example adds an additional query that iterates through the collection of anonymous types and projects a new collection of an anonymous type with the addition of a new member, Text
. It uses the Aggregate standard query operator to concatenate multiple strings into one string.
This technique (that is, first projecting to a collection of an anonymous type, then using this collection to project to a new collection of an anonymous type) is a common and useful one. This query could have been written without projecting to the first anonymous type. However, because of lazy evaluation, doing so doesn't use much additional processing power. The technique does create more short-lived objects on the heap, but that doesn't substantially degrade performance.
Of course, it would be possible to write a single query that contains the functionality to retrieve the paragraphs, the style of each paragraph, and the text of each paragraph. However, it's often useful to break up a more complicated query into multiple queries because the resulting code is more modular and easier to maintain. Further, if you need to reuse a portion of the query, it's easier to refactor if the queries are written in this manner.
These queries, which are chained together, use the processing model that's examined in the article Chain queries example.
Example: Determine the element node, style name, and text of each paragraph
This example processes a WordprocessingML document, determining the element node, style name, and text of each paragraph. This example builds on the previous examples in this tutorial. The new query is called out in comments in the code below.
The instructions for creating the source document for this example are in Create the source Office Open XML document.
This example uses classes from the WindowsBase assembly. It uses types in the System.IO.Packaging namespace.
const string fileName = "SampleDoc.docx";
const string documentRelationshipType =
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
const string stylesRelationshipType =
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles";
const string wordmlNamespace =
"http://schemas.openxmlformats.org/wordprocessingml/2006/main";
XNamespace w = wordmlNamespace;
XDocument xDoc = null;
XDocument styleDoc = null;
using (Package wdPackage = Package.Open(fileName, FileMode.Open, FileAccess.Read))
{
PackageRelationship docPackageRelationship =
wdPackage.GetRelationshipsByType(documentRelationshipType).FirstOrDefault();
if (docPackageRelationship != null)
{
Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative),
docPackageRelationship.TargetUri);
PackagePart documentPart = wdPackage.GetPart(documentUri);
// Load the document XML in the part into an XDocument instance.
xDoc = XDocument.Load(XmlReader.Create(documentPart.GetStream()));
// Find the styles part. There will only be one.
PackageRelationship styleRelation =
documentPart.GetRelationshipsByType(stylesRelationshipType).FirstOrDefault();
if (styleRelation != null)
{
Uri styleUri = PackUriHelper.ResolvePartUri(documentUri, styleRelation.TargetUri);
PackagePart stylePart = wdPackage.GetPart(styleUri);
// Load the style XML in the part into an XDocument instance.
styleDoc = XDocument.Load(XmlReader.Create(stylePart.GetStream()));
}
}
}
string defaultStyle =
(string)(
from style in styleDoc.Root.Elements(w + "style")
where (string)style.Attribute(w + "type") == "paragraph"&&
(string)style.Attribute(w + "default") == "1"
select style
).First().Attribute(w + "styleId");
// Find all paragraphs in the document.
var paragraphs =
from para in xDoc
.Root
.Element(w + "body")
.Descendants(w + "p")
let styleNode = para
.Elements(w + "pPr")
.Elements(w + "pStyle")
.FirstOrDefault()
select new
{
ParagraphNode = para,
StyleName = styleNode != null ?
(string)styleNode.Attribute(w + "val") :
defaultStyle
};
// Following is the new query that retrieves the text of
// each paragraph.
var paraWithText =
from para in paragraphs
select new
{
ParagraphNode = para.ParagraphNode,
StyleName = para.StyleName,
Text = para
.ParagraphNode
.Elements(w + "r")
.Elements(w + "t")
.Aggregate(
new StringBuilder(),
(s, i) => s.Append((string)i),
s => s.ToString()
)
};
foreach (var p in paraWithText)
Console.WriteLine("StyleName:{0} >{1}<", p.StyleName, p.Text);
Imports <xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
Module Module1
' Following function is required because Visual Basic doesn't support short circuit evaluation
Private Function GetStyleOfParagraph(ByVal styleNode As XElement, _
ByVal defaultStyle As String) As String
If (styleNode Is Nothing) Then
Return defaultStyle
Else
Return styleNode.@w:val
End If
End Function
Sub Main()
Dim fileName = "SampleDoc.docx"
Dim documentRelationshipType = _
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
Dim stylesRelationshipType = _
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"
Dim wordmlNamespace = _
"http://schemas.openxmlformats.org/wordprocessingml/2006/main"
Dim xDoc As XDocument = Nothing
Dim styleDoc As XDocument = Nothing
Using wdPackage As Package = Package.Open(fileName, FileMode.Open, FileAccess.Read)
Dim docPackageRelationship As PackageRelationship = _
wdPackage.GetRelationshipsByType(documentRelationshipType).FirstOrDefault()
If (docPackageRelationship IsNot Nothing) Then
Dim documentUri As Uri = PackUriHelper.ResolvePartUri(New Uri("/", UriKind.Relative), _
docPackageRelationship.TargetUri)
Dim documentPart As PackagePart = wdPackage.GetPart(documentUri)
' Load the document XML in the part into an XDocument instance.
xDoc = XDocument.Load(XmlReader.Create(documentPart.GetStream()))
' Find the styles part. There will only be one.
Dim styleRelation As PackageRelationship = _
documentPart.GetRelationshipsByType(stylesRelationshipType).FirstOrDefault()
If (styleRelation IsNot Nothing) Then
Dim styleUri As Uri = _
PackUriHelper.ResolvePartUri(documentUri, styleRelation.TargetUri)
Dim stylePart As PackagePart = wdPackage.GetPart(styleUri)
' Load the style XML in the part into an XDocument instance.
styleDoc = XDocument.Load(XmlReader.Create(stylePart.GetStream()))
End If
End If
End Using
Dim defaultStyle As String = _
( _
From style In styleDoc.Root.<w:style> _
Where style.@w:type = "paragraph" And _
style.@w:default = "1" _
Select style _
).First().@w:styleId
' Find all paragraphs in the document.
Dim paragraphs = _
From para In xDoc.Root.<w:body>...<w:p> _
Let styleNode As XElement = para.<w:pPr>.<w:pStyle>.FirstOrDefault _
Select New With { _
.ParagraphNode = para, _
.StyleName = GetStyleOfParagraph(styleNode, defaultStyle) _
}
' Following is the new query that retrieves the text of
' each paragraph.
Dim paraWithText = _
From para In paragraphs _
Select New With { _
.ParagraphNode = para.ParagraphNode, _
.StyleName = para.StyleName, _
.Text = para.ParagraphNode.<w:r>.<w:t> _
.Aggregate(New StringBuilder(), _
Function(s As StringBuilder, i As String) s.Append(CStr(i)), _
Function(s As StringBuilder) s.ToString) _
}
For Each p In paraWithText
Console.WriteLine("StyleName:{0} >{1}<", p.StyleName, p.Text)
Next
End Sub
End Module
The C# version of this example produces the following output:
StyleName:Heading1 >Parsing WordprocessingML with LINQ to XML<
StyleName:Normal ><
StyleName:Normal >The following example prints to the console.<
StyleName:Normal ><
StyleName:Code >using System;<
StyleName:Code ><
StyleName:Code >class Program {<
StyleName:Code > public static void (string[] args) {<
StyleName:Code > Console.WriteLine("Hello World");<
StyleName:Code > }<
StyleName:Code >}<
StyleName:Normal ><
StyleName:Normal >This example produces the following output:<
StyleName:Normal ><
StyleName:Code >Hello World<
The Visual Basic version of this example produces the following output:
StyleName:Heading1 >Parsing WordprocessingML with LINQ to XML<
StyleName:Normal ><
StyleName:Normal >The following example prints to the console.<
StyleName:Normal ><
StyleName:Code >Imports System<
StyleName:Code ><
StyleName:Code >Class Program<
StyleName:Code > Public Shared Sub Main(ByVal args() As String)<
StyleName:Code > Console.WriteLine("Hello World")<
StyleName:Code > End Sub<
StyleName:Code >End Class<
StyleName:Normal ><
StyleName:Normal >This example produces the following output:<
StyleName:Normal ><
StyleName:Code >Hello World<
The next article in this tutorial shows how to use an extension method, instead of Aggregate, to concatenate multiple strings into a single string: