Redgate Hub

  • Åtkomst till XML-data i PowerShell
    • Åtkomst till XML med XPath.
    • Accessing XML as Objects.
    • Komparation av XPath- och objektmetoder
  • Modifiera eller skapa XML-data
  • Lägga till XML-data
  • Använda XML för objektserialisering

Introduktion

Som alla andra högnivåsamtal är the data kärnan i PowerShell. I PowerShells fall handlar det om att konvertera externa data till PowerShell-objekt och vice versa. Det här är den andra artikeln i en serie artiklar som visar hur du importerar nästan alla vanliga dataformat som du kan tänkas stöta på, och hur du exporterar till några av dem också. Den första artikeln i serien, PowerShell Data Basics: File-Based Data: File-Based Data, täcker en mängd olika textformat, från filer med fast bredd, variabel bredd och ragged-right-filer till CSV, egenskapslistor, INI-filer och JSON-data, och avslutas med en behandling av import och export till Excel. Här koncentrerar vi oss på att få ut det mesta av XML.

Accessing XML data in PowerShell

Det finns två inbyggda tekniker för att arbeta med XML-data i PowerShell; XPath-metoden och objektsystemet med punktnotering. Vi kommer att beskriva och jämföra dessa två tillvägagångssätt och prova dem på några XML-exempel. För enkelhetens skull används den här XML-provfilen från MSDN i alla kodexempel. Figur 1 visar en representation av schemat som ligger till grund för filen i ett kortfattat format (särskilt om man jämför med att läsa den råa XSD-filen!), tack vare Visual Studios XML Schema Explorer. I figuren ser du att XML-filen är en katalog som innehåller en samling böcker. Varje bok har sju egenskaper, varav sex är barnelement och en är ett barnattribut.

Figur 1 Schema för Microsofts XML-exempelfilo:p>

För att läsa in den här XML-exempelfilen kan du använda något av följande:

Om du föredrar att experimentera med omedelbar XML-data i stället för att läsa in en XML-fil i ett XmlDocument, är det enkelt att göra. Du behöver bara definiera din XML som en sträng och kasta den till XML-typ på samma sätt som vi just har gjort med Get-Content-cmdletten för filer. Här är en del av XML-provfilen med endast två böcker:

Accessing XML with XPath

Med filen inläst i ett XmlDocument-objekt kan du sedan navigera i XML-trädet med XPath. För att välja en uppsättning noder använder du metoden SelectNodes:

$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

Du kan också använda SelectSingleNode för att returnera endast en nod:

$xdoc.SelectSingleNode(”//book”)

id : bk102

author : Ralls, Kim

titel : Midnight Rain

genre : Fantasy

Pris : 5.95

publish_date : 2000-12-16

description : En före detta arkitekt kämpar mot företagszombies,

en ond trollkvinna och sin egen barndom för att bli världens drottning

.

Bemärk i det första exemplet att det finns dubbletter. Anta att du istället vill ha en lista över unika författare i katalogen. Du kanske tänker att något som …

1
$xdoc.SelectNodes(”//author”) | select -Unique

…skulle fungera, men det returnerar faktiskt bara den första författarens namn. För att förstå varför det misslyckades måste du förstå mer om XML-dokumentens struktur. Det första exemplet returnerade faktiskt en lista med textnoder (dvs. inte en lista med text) och det unika filtret arbetar på den listan och letar efter unika elementtyper. Eftersom alla objekt i samlingen är textnoder och därmed alla har samma elementtyp anses alla noder efter den första därför vara dubbletter. Resultatet blir att endast den första återges.

Det du egentligen är ute efter är strängvärdet för varje författarnod. Från författarnoden måste du först komma åt dess textnod (dess första och enda barn) och sedan värdet av denna textnod (rad A i nästa exempel). Alternativt kan du använda ett något kortare uttryck med egenskapen InnerText (rad B). Ytterligare en variant använder cmdlet Select-Xml som undviker ett metodanrop och som på sätt och vis är en mer distinkt PowerShell-metod (rad C). Alla tre rader returnerar samma resultat.

SelectNodes och SelectSingleNode ger dig tillsammans motsvarande funktionalitet som Select-Xml. Båda har stöd för namespaces, vilket jag ännu inte har nämnt. De två metoderna tar båda en XmlNamespaceManager som en valfri andra parameter, medan cmdlet Select-Xml tar en valfri Namespace-parameter som anger dina namespaces i en hashtabell.

SelectSingleNode returnerar en XmlNode och SelectNodes returnerar en XmlNodeList. Select-Xml å andra sidan returnerar ett SelectXmlInfo-objekt (eller en array av dem) och dess Node-egenskap ger tillgång till den underliggande noden. Exemplet precis ovan illustrerar dessa skillnader.

Accessing XML as Objects

Med samma XmlDocument-objekt som i förra avsnittet ger PowerShell också dynamiskt objektstöd för XML-data: Detta gör att du kan få tillgång till XML-data som förstklassiga PowerShell-objekt, vilket varken kräver XPath-selektor eller förtrogenhet med detaljerna i sådana saker som XML-noder, värden eller textnoder. Dessutom får du omedelbar, dynamisk Intellisense av ditt XML-schema när du laddar dina XML-data! Figur 2 illustrerar detta för PowerShell ISE, där du får både valmöjligheter och ordkomplettering, precis som med PowerShells inhemska tokens. Lägg särskilt märke till i den nedre expansionen att den letar efter vad du har skrivit var som helst i egenskapsnamnet, inte bara från och med det första tecknet: Intellisense skulle hitta en datumegenskap här oavsett om den heter published_date eller releaseDate eller date_of_publication. Observera att du har ordkomplettering tillgänglig i PowerShell V2 eller V3 och i PowerShell ISE eller PowerShell-konsolen. Men valmöjligheterna är endast tillgängliga i PowerShell ISE i V3.

Figur 2 Automatisk Intellisense vid inläsning av ett XML-dokument

Här är några exempel för att visa att XML verkligen autokonverteras till PowerShell-objekt:

$xdoc

xml catalog

– —

version=”1.0″ catalog

$xdoc.catalog

book

{book, book, book, book…}

$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 fördjupning …

bk102 Ralls, Kim Midnight Rain Fantasy 5.95 2000-12-16 En före detta arkitekt …

bk103 Corets, Eva Maeve Ascendant Fantasy 5.95 2000-11-17 Efter kollapsen …

bk104 Corets, Eva Oberon’s Legacy Fantasy 5.95 2001-03-10 I postapokalyp…

bk105 Corets, Eva The Sundered Grail Fantasy 5.95 2001-09-10 De två döttrarna…

bk106 Randall, Cynthia Lover Birds Romantik 4.95 2000-09-02 När Carla möter …

bk107 Thurman, Paula Splish Splash Romantik 4.95 2000-11-02 En djuphavsdykare …

bk108 Knorr, Stefan Creepy Crawlies Horror 4.95 2000-12-06 En antologi av h…

bk109 Kress, Peter Paradox Lost Science Fiction 6.95 2000-11-02 Efter att en oavsiktlig…

bk110 O’Brien, Tim Microsoft .NET: 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

författare : Corets, Eva

titel : Maeve Ascendant

genre : Fantasy

Pris : 5.95

publish_date : 2000-11-17

description : Efter kollapsen av ett nanoteknologiskt

samhälle i England, lägger de unga överlevarna

grunden för ett nytt samhälle

$xdoc.catalog.book.author

Randall, Cynthia

$xdoc.catalog.book.id

bk106

Observera att alla XML-noder i dokumentet konverteras till standard PowerShell-egenskaper, oavsett om en nod har barn (t.ex. catalog) eller är en bladnod (t.ex. price), eller om en bladnod är ett element (t.ex. author) eller ett attribut (t.ex. id). I synnerhet (som de två sista exemplen ovan illustrerar) behandlas elementvärden och attributvärden exakt likadant med standardnotationen ”dot”.

Varje jämförelse mellan XPath- och objektsmetoderna

Vilket tillvägagångssätt är bättre för att få tillgång till XML-data? Tabell 1 hjälper dig att besvara denna fråga. Objektmetoden är vanligtvis mer kortfattad (t.ex. rad 3), men inte alltid (rad 4). XPath är dock mer uttrycksfull, eftersom det gör det möjligt att ange vissa selektorer som inte är möjliga med objektsnotationen (rad 7). PowerShells egna möjligheter kan dock lätt fylla denna lucka när man använder objektsnotering (rad 8).

Tabell 1 Jämförelse av XPath- och objektselektormetoder för XML-åtkomst.

Ändra eller skapa XML-data

Med tanke på att man har en förståelse för XML-selektorer från de tidigare avsnitten, är det ganska okomplicerat att ändra ett XML-dokument eftersom XML-selektorer (antingen XPath eller objekt) är L-värden, dvs. man kan skriva till dem såväl som läsa från dem! Endera av dessa kommer alltså att ändra författaren till den sjätte boken:

1
2

$xdoc.SelectSingleNode(”//book/author”).InnerText = ’jones’
$xdoc.catalog.book.author = ’smith’

Ganska ofta kan det hända att du har en befintlig XML-fil där du vill ändra ett eller flera nodvärden. Du vill förmodligen läsa filen, ändra data och spara tillbaka filen med samma namn. Du har sett de två första stegen; det tredje steget görs med Save-metoden på XmlDocument. Genom att sätta ihop allt detta får man den här grundläggande koden:

Den här koden går bra – förutom att den för det mesta ser ut att ha misslyckats! Den här enkla delen av koden illustrerar en till synes obetydlig men viktig PowerShell-föreställning; okunskap om detta har lett till många blogginlägg där det påstås att det är en bugg i PowerShell. Problemet är att din arbetskatalog och din PowerShell-plats inte är samma sak. Ovanstående kod läser filen helt okej, den ändrar data helt okej, men den sparar inte nödvändigtvis den nya filen där du förväntar dig att den ska göra det. Get-Content, som är en PowerShell-cmdlet, ser en filsökväg som är relativ till PowerShell-platsen. Metoden XmlDocument.Save däremot ser en filsökväg i förhållande till PowerShell-processens arbetskatalog eftersom metodanropet ligger utanför PowerShell. Om du inte har utfört Set-Location (eller dess alias cd) i den aktuella PowerShell-sessionen pekar båda på samma katalog. För att bekräfta detta utför du de här två instruktionerna:

1
2

Get-Location # visar PowerShell-plats
::CurrentDirectory # visar arbetskatalogen

Det säkraste tillvägagångssättet är alltså att använda absoluta sökvägar för att undvika det här problemet helt och hållet. Se Alex Angelopoulos artikel Why the PowerShell Working Directory and the PowerShell Location Aren’t One in the Same (varför PowerShell Working Directory och PowerShell Location inte är samma sak) för mer information.

Lägg till XML-data

Att lägga till nya noder i XML-dokumentet kräver bara lite mer arbete än att ändra värdet på en befintlig nod. Ett tillvägagångssätt som jag gillar kommer från Tobias Weltners blogginlägg Write, Add and Change XML Data: ta en befintlig nod av den typ som du vill skapa, gör en kopia av den noden och modifiera kopian med dina nya data, och infoga slutligen den kopierade noden i ditt XML-dokument som ett syskon till originalet. Om vi tillämpar detta på vårt exempel med bokkatalogen skapar den här delen av koden en ny boknod och lägger till den i slutet av samlingen:

Om du vill lägga till den nya boken på en annan plats, till exempel efter den tredje boken, använder du InsertAfter istället för AppendChild:

1
$xdoc.catalog.InsertAfter($book,$xdoc.catalog.book)

(Namnet på metoden InsertAfter förnekar det faktum att den inte bara lägger till noder; den kan också flytta noder! Metoden kontrollerar om noden du vill lägga till redan finns i dokumentet. Om så är fallet flyttar den den till den nya plats som du anger. När du vill kopiera noder måste du alltså börja med Clone-metoden som illustreras ovan.)

För ytterligare utforskning av hur man manipulerar XML-data med .NET-metoder, se Process XML-data med hjälp av DOM-modellen på MSDN.

Användning av XML för objektserialisering

PowerShell erbjuder ett enkelt sätt att bevara objekt genom att använda Export-Clixml för att serialisera ett objekt och lagra det i en XML-fil och Import-Clixml för att återställa objektet från XML. Med XML, till skillnad från de flesta andra serialiseringstekniker, bevaras objektens integritet: när ett objekt återställs från XML är alla egenskaper korrekt typade, liksom själva det överordnade objektet, så alla metoder på det ursprungliga objektet är tillgängliga även på det återskapade objektet. För att använda Export-Clixml är det bara att pipa en objektsamling till den och ange en destinationsfil. Här är ett enkelt exempel som visar att resultatet från Get-ChildItem, en samling FileSystemInfo-objekt, regenereras:

Lämna ett svar

Din e-postadress kommer inte publiceras.