Udostępnij za pośrednictwem


Pobieranie akapitów i ich stylów (LINQ to XML)

W tym przykładzie piszemy zapytanie, które pobiera węzły akapitu z dokumentu WordprocessingML. Identyfikuje również styl każdego akapitu.

To zapytanie opiera się na zapytaniu w poprzednim przykładzie Znajdź domyślny styl akapitu, który pobiera domyślny styl z listy stylów. Te informacje są wymagane, aby zapytanie mogło zidentyfikować styl akapitów, które nie mają jawnie ustawionego stylu. Style akapitu w:pPr są ustawiane przez element; jeśli akapit nie zawiera tego elementu, jest sformatowany przy użyciu stylu domyślnego.

W tym artykule wyjaśniono znaczenie niektórych fragmentów zapytania, a następnie pokazano zapytanie w ramach kompletnego przykładu roboczego.

Przykład: pobieranie wszystkich akapitów w dokumencie oraz style akapitu

Poniższy kod to zapytanie służące do pobierania wszystkich akapitów w dokumencie i ich stylach:

xDoc.Root.Element(w + "body").Descendants(w + "p")
xDoc.Root.<w:body>...<w:p>

To wyrażenie jest podobne do źródła zapytania w poprzednim przykładzie Znajdź domyślny styl akapitu. Główną różnicą jest użycie Descendants osi zamiast Elements osi. Zapytanie używa Descendants osi, ponieważ w dokumentach, które zawierają sekcje, akapity nie będą bezpośrednimi elementami podrzędnymi elementu treści, a akapity będą dwoma poziomami w hierarchii. Korzystając z Descendants osi, kod będzie działać niezależnie od tego, czy dokument używa sekcji.

Przykład: użyj klauzuli let , aby określić element zawierający węzeł stylu

Następujące zapytanie używa klauzuli let , aby określić element zawierający węzeł stylu:

let styleNode = para.Elements(w + "pPr").Elements(w + "pStyle").FirstOrDefault()
Let styleNode As XElement = para.<w:pPr>.<w:pStyle>.FirstOrDefault()

Klauzula let (Letw wersji języka Visual Basic) najpierw używa Elements osi do znajdowania wszystkich elementów o nazwie , a następnie używa Elements metody rozszerzenia do znajdowania wszystkich elementów podrzędnych o nazwie pPrpStyle, a na koniec używa standardowego FirstOrDefault operatora zapytania, aby przekonwertować kolekcję na pojedynczą. Jeśli kolekcja jest pusta, styleNode jest ustawiona na null (Nothing w wersji Visual Basic). Jest to przydatny idiom do wyszukiwania węzła podrzędnego pStyle . Należy pamiętać, że jeśli pPr węzeł podrzędny nie istnieje, kod nie powiedzie się, zgłaszając wyjątek; zamiast tego styleNode jest ustawiona wartość null (Nothing), która jest pożądanym zachowaniem tej letklauzuli (Let).

Jeśli nie ma żadnego elementu, styleNode zostanie ustawiona wartość null (Nothing).

Zapytanie projektuje kolekcję typu anonimowego z dwoma elementami członkowskimi StyleName i ParagraphNode.

Przykład: pobieranie węzłów akapitu z dokumentu WordprocessingML

Ten przykład przetwarza dokument WordprocessingML, pobierając węzły akapitu. Identyfikuje również styl każdego akapitu. Ten przykład opiera się na poprzednich przykładach w tym samouczku. Nowe zapytanie jest wywoływane w komentarzach w poniższym kodzie.

Instrukcje dotyczące tworzenia dokumentu źródłowego dla tego przykładu znajdują się w artykule Tworzenie źródłowego dokumentu Office Open XML.

W tym przykładzie użyto klas znalezionych w zestawie WindowsBase. Używa typów w System.IO.Packaging przestrzeni nazw.

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");

// Following is the new query that finds all paragraphs in the
// document and their styles.
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
    };

foreach (var p in paragraphs)
    Console.WriteLine("StyleName:{0}", p.StyleName);
Imports <xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">

Module Module1
    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

        ' Following is the new query that finds all paragraphs in the
        ' document and their styles.
        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) _
        }

        For Each p In paragraphs
            Console.WriteLine("StyleName:{0}", p.StyleName)
        Next

    End Sub
End Module

Ten przykład generuje następujące wyniki:

StyleName:Heading1
StyleName:Normal
StyleName:Normal
StyleName:Normal
StyleName:Code
StyleName:Code
StyleName:Code
StyleName:Code
StyleName:Code
StyleName:Code
StyleName:Code
StyleName:Normal
StyleName:Normal
StyleName:Normal
StyleName:Code

W następnym artykule w tym samouczku pokazano, jak utworzyć zapytanie w celu pobrania tekstu akapitów:

Zobacz też