- Adgang til XML-data i PowerShell
- Accessing XML with XPath.
- Accessing XML as Objects (Adgang til XML som objekter).
- Sammenligning af XPath- og objekttilgange
- Ændring eller oprettelse af XML-data
- Tilføjelse af XML-data
- Anvendelse af XML til objektserialisering
Indledning
Som med ethvert højniveausprog er the data
kernen i PowerShell. I PowerShells tilfælde går det i korthed ud på at konvertere eksterne data til PowerShell-objekter og omvendt. Dette er den anden artikel i en serie af artikler, der viser dig, hvordan du importerer næsten alle de almindelige dataformater, du sandsynligvis vil støde på, og hvordan du også eksporterer til nogle af dem. Den første artikel i serien, PowerShell Data Basics: File-Based Data: File-Based Data, dækker en række tekstformater, fra filer med fast bredde, variabel bredde og ragged-right-filer til CSV, egenskabslister, INI-filer og JSON-data, og afsluttes med en behandling af import og eksport til Excel. Her koncentrerer vi os om at få mest muligt ud af XML.
Accessing XML data in PowerShell
Der er to indbyggede teknikker til at arbejde med XML-data i PowerShell; XPath-tilgangen og objektpunkt-notation-tilgangen. Vi vil beskrive og sammenligne disse to fremgangsmåder og afprøve dem på nogle eksempler på XML. For nemheds skyld bruger alle kodeeksemplerne denne XML-eksempelfil fra MSDN. Figur 1 viser en repræsentation af det skema, der ligger til grund for filen, i et kortfattet format (især i forhold til at læse den rå XSD-fil!), takket være Visual Studio’s XML Schema Explorer. På figuren kan du se, at XML-filen er et katalog, der indeholder en samling af bøger. Hver bog har syv egenskaber, hvoraf seks er underordnede elementer og én er en underordnet attribut.
Figur 1 Skema for Microsofts XML-eksempelfilo:p>
For at indlæse denne XML-eksempelfil kan du bruge en af disse:
Hvis du foretrækker at eksperimentere med umiddelbare XML-data i stedet for at indlæse en XML-fil i et XmlDocument, er det nemt at gøre. Du skal blot definere din XML som en streng og kaste den til XML-typen, som vi netop har gjort det med Get-Content-cmdletten til filer. Her er en del af XML-eksempelfilen med kun to bøger:
Accessing XML with XPath
Med filen indlæst i et XmlDocument-objekt kan du derefter navigere i XML-træet med XPath. Hvis du vil vælge et sæt knuder, skal du bruge SelectNodes-metoden:
$xdoc.SelectNodes(“//author”)
#text
—
Gambardella, Matthew
Ralls, Kim
Corets, Eva
Corets, Eva
Corets, Eva
Corets, Eva
Randall, Cynthia
Thurman, Paula
Knorr, Stefan
Kress, Peter
O’Brien, Tim
O’Brien, Tim
Galos, Mike
Eller brug SelectSingleNode for kun at returnere en enkelt knude:
$xdoc.SelectSingleNode(“//book”)
id : bk102
author : Ralls, Kim
title : Midnight Rain
genre : Fantasy
Pris : 5.95
publish_date : 2000-12-16
description : En tidligere arkitekt kæmper mod virksomhedens zombier,
en ond troldkvinde og sin egen barndom for at blive dronning
af verden.
Bemærk i det første eksempel, at der er dubletter. Lad os antage, at du i stedet ønsker en liste over unikke forfattere i kataloget. Du tænker måske, at noget som …
1
|
$xdoc.SelectNodes(“//author”) | select -Unique
|
…ville virke, men den returnerer faktisk kun den første forfatters navn. For at forstå, hvorfor det mislykkedes, skal du forstå mere om strukturen af XML-dokumenter. Det første eksempel returnerede faktisk en liste af tekstknuder (dvs. ikke en liste af tekst), og det unikke filter opererer på denne liste og leder efter elementtypens entydighed. Da alle elementer i samlingen er tekstnoder og dermed alle er af samme elementtype, betragtes alle noder ud over den første derfor som dubletter. Resultatet er, at kun det første element returneres.
Det, du i virkeligheden er ude efter, er strengværdien for hver forfatterknude. Fra forfatterknuden skal du først få adgang til dens tekstknude (dens første og eneste barn) og derefter til værdien af denne tekstknude (linje A i det næste eksempel). Alternativt kan du bruge et lidt kortere udtryk med InnerText-egenskaben (linje B). Endnu en variant bruger Select-Xml-cmdletten, som undgår et metodekald og derfor på en vis måde er en mere karakteristisk PowerShell-tilgang (linje C). Alle tre linjer returnerer det samme resultat.
SelectNodes og SelectSingleNode giver dig tilsammen en funktionalitet, der svarer til Select-Xml. Begge understøtter namespaces, som jeg endnu ikke har nævnt. De to metoder tager begge en XmlNamespaceManager som en valgfri anden parameter, mens Select-Xml-cmdletten tager en valgfri parameter Namespace, der angiver dine namespaces i en hashtabel.
SelectSingleNode returnerer en XmlNode, og SelectNodes returnerer en XmlNodeList. Select-Xml returnerer på den anden side et SelectXmlInfo-objekt (eller et array af dem), og dets Node-egenskab giver adgang til den underliggende knude. Eksemplet lige ovenfor illustrerer disse forskelle.
Accessing XML as Objects
Med det samme XmlDocument-objekt fra sidste afsnit giver PowerShell også dynamisk objektunderstøttelse for XML-data: Dette giver dig mulighed for at få adgang til XML-data som førsteklasses PowerShell-objekter, hvilket hverken kræver XPath-selektor eller kendskab til detaljerne i ting som XML-knuder, værdier eller tekstknuder. Desuden får du øjeblikkelig, dynamisk Intellisense af dit XML-skema, når du indlæser dine XML-data! Figur 2 illustrerer dette for PowerShell ISE, hvor du får både valgmuligheder og ordkomplettering, ligesom med PowerShell native tokens. Bemærk især i den nederste udvidelse, at den leder efter det, du har indtastet overalt i egenskabsnavnet, og ikke kun fra det første tegn: Intellisense ville finde dig en datoegenskab her, uanset om den hedder published_date eller releaseDate eller date_of_publication. Bemærk, at du har ordkomplettering til rådighed i PowerShell V2 eller V3, og i PowerShell ISE eller PowerShell-konsollen. Men valgmulighederne er kun tilgængelige i PowerShell ISE i V3.
Figur 2 Automatisk Intellisense ved indlæsning af et XML-dokument
Her er nogle eksempler for at vise, at XML faktisk automatisk konverteres til PowerShell-objekter:
$xdoc
xml catalog
– —
version=”1.0″ katalog
$xdoc.catalog
bog
—
{bog, bog, bog, bog, bog…}
$xdoc.catalog.book | Format-Table -AutoSize
id author title genre price publish_date description
– — — — — — — —- —-
bk101 Gambardella, Matthew XML Developer’s Guide Computer 44.95 2000-10-01 En dybdegående gennemgang …
bk102 Ralls, Kim Midnight Rain Fantasy 5.95 2000-12-16 En tidligere arkitekt …
bk103 Corets, Eva Maeve Ascendant Fantasy 5.95 2000-11-17 Efter sammenbruddet …
bk104 Corets, Eva Oberon’s Legacy Fantasy 5.95 2001-03-10 I post-apokalypse…
bk105 Corets, Eva The Sundered Grail Fantasy 5.95 2001-09-10 De to døtre…
bk106 Randall, Cynthia Lover Birds Romantik 4.95 2000-09-02 Da Carla møder …
bk107 Thurman, Paula Splish Splash Romantik 4.95 2000-09-02 Da Carla møder …
bk107 Thurman, Paula Splish Splash Romantik 4.95 2000-11-02 En dybhavsdykker …
bk108 Knorr, Stefan Creepy Crawlies Horror 4.95 2000-12-06 En antologi af h…
bk109 Kress, Peter Paradox Lost Science Fiction 6.95 2000-11-02 Efter en utilsigtet…
bk110 O’Brien, Tim Microsoft .NET: The Prog… Computer 36,95 2000-12-09 Microsofts .NET …
bk111 O’Brien, Tim MSXML3: A Comprehensive … Computer 36,95 2000-12-01 The Microsoft MSX…
bk112 Galos, Mike Visual Studio 7: A Compr… Computer 49.95 2001-04-16 Microsoft Visual …
$xdoc.catalog.book
id : bk103
forfatter : Corets, Eva
title : Maeve Ascendant
genre : Fantasy
Pris : 5.95
publish_date : 2000-11-17
description : Efter sammenbruddet af et nanoteknologisk
samfund i England, lægger de unge overlevende det
fundament for et nyt samfund
$xdoc.catalog.book.author
Randall, Cynthia
$xdoc.catalog.book.id
bk106
Bemærk, at alle XML-knuder i dokumentet konverteres til standard PowerShell-egenskaber, uanset om en knude har børn (f.eks. catalog) eller er en bladknude (f.eks. price), eller om en bladknude er et element (f.eks. author) eller en attribut (f.eks. id). Især (som de to sidste eksempler ovenfor illustrerer) behandles elementværdier og attributværdier nøjagtigt ens med standard “dot”-notationen.
Sammenligning af XPath- og objekttilgange
Hvilken tilgang er bedst til at få adgang til XML-data? Tabel 1 hjælper dig med at besvare dette spørgsmål. Objekttilgangen er normalt mere kortfattet (f.eks. linje 3), men ikke altid (linje 4). XPath er imidlertid mere udtryksfuld, idet den giver mulighed for at angive nogle selektorer, som ikke er mulige med objektnotationen (linje 7). PowerShells egne muligheder kan dog nemt udfylde dette hul ved brug af objektnotation (linje 8).
Tabel 1 Sammenligning af XPath- og objekt-selektortilgange til XML-adgang.
Ændring eller oprettelse af XML-data
Givet en forståelse af XML-selektorer fra de foregående afsnit er det ret ligetil at ændre et XML-dokument, fordi XML-selektorer (enten XPath eller objekt) er L-værdier, dvs. du kan både skrive til dem og læse fra dem! En af disse vil således ændre forfatteren til den 6. bog:
1
2
|
$xdoc.SelectSingleNode(“//book/author”).InnerText = ‘jones’
$xdoc.catalog.book.author = ‘smith’
|
Som oftest har du måske en eksisterende XML-fil, hvor du ønsker at ændre en eller flere nodeværdier. Du vil sandsynligvis gerne læse filen, ændre dataene og gemme filen tilbage under samme navn. Du har set de to første trin; det tredje trin udføres med Save-metoden på XmlDocument. Ved at sætte det hele sammen får man altså denne grundlæggende kode:
Denne kode kører fint – bortset fra, at det meste af tiden vil det se ud til, at den er mislykkedes! Denne enkle del af koden illustrerer et tilsyneladende mindre, men vigtigt PowerShell-begreb; uvidenhed om dette har ført til mange blogindlæg, der hævder, at det er en fejl i PowerShell. Problemet er, at din arbejdsmappe og din PowerShell-placering ikke er den samme ting. Ovenstående kode læser filen fint, den ændrer dataene fint, men den gemmer ikke nødvendigvis den nye fil der, hvor du forventer, at den skal. Get-Content, som er en PowerShell-cmdlet, ser en filsti relativt til PowerShell-placeringen. XmlDocument.Save-metoden ser derimod en filsti relativt til PowerShell-processens arbejdsmappe, fordi dette metodekald er uden for PowerShell. Hvis du ikke har udført Set-Location (eller dens alias cd) i din aktuelle PowerShell-session, peger begge på den samme mappe. Du kan bekræfte dette ved at udføre disse to udsagn:
1
2
|
Get-Location # viser PowerShell-lokation
::CurrentDirectory # viser arbejdsmappe
|
Den sikreste fremgangsmåde er derfor at bruge absolutte stier for at undgå dette problem helt og holdent. Se Alex Angelopoulos’ artikel Why the PowerShell Working Directory and the PowerShell Location Aren’t One in the Same (hvorfor PowerShell-arbejdskataloget og PowerShell-placeringen ikke er det samme) for at få mere at vide.
Tilføjelse af XML-data
Det kræver bare lidt mere arbejde at tilføje nye noder til dit XML-dokument end at ændre værdien af en eksisterende node. En fremgangsmåde, som jeg godt kan lide, stammer fra Tobias Weltners blogindlæg Write, Add and Change XML Data: Tag en eksisterende node af den type, som du ønsker at oprette, lav en kopi af denne node og modificer kopien med dine nye data, og indsæt til sidst den kopierede node i din XML som en søskende til den oprindelige. Hvis vi anvender dette på vores eksempel med bogkataloget, opretter denne kode en ny bognode og føjer den til slutningen af samlingen:
Hvis du ønsker at tilføje den nye bog et andet sted, f.eks. efter den 3. bog, skal du bruge InsertAfter i stedet for AppendChild:
1
|
$xdoc.catalog.InsertAfter($book,$xdoc.catalog.book)
|
(Navnet på metoden InsertAfter skjule, at den ikke kun tilføjer knuder; den kan også flytte knuder! Metoden kontrollerer, om den node, du beder om at tilføje, allerede findes i dokumentet. Hvis det er tilfældet, flytter den den til den nye placering, som du angiver. Når du således ønsker at kopiere knuder, skal du starte med Clone-metoden som illustreret ovenfor.)
For yderligere udforskning af manipulation af XML-data med .NET-metoder, se Process XML-data ved hjælp af DOM-modellen på MSDN.
Brug af XML til objektserialisering
PowerShell giver en nem måde at bevare objekter på ved at bruge Export-Clixml til at serialisere ethvert objekt og gemme det i en XML-fil og Import-Clixml til at gendanne objektet fra XML. Med XML bevares objektintegriteten i modsætning til de fleste andre serialiseringsteknikker: Ved gendannelse af et objekt fra XML er alle egenskaber korrekt typet ligesom selve det overordnede objekt, så alle metoder på det oprindelige objekt er også tilgængelige på det regenererede objekt. Hvis du vil bruge Export-Clixml, skal du blot sende en hvilken som helst objektsamling via pipe til den og angive en destinationsfil. Her er et simpelt eksempel, der viser, at output fra Get-ChildItem, en samling af FileSystemInfo-objekter, regenereres: