Redgate Hub

  • 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:

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.