Werken met XML in Groovy

1. Inleiding

Groovy biedt een aanzienlijk aantal methoden voor het doorlopen en manipuleren van XML-inhoud.

In deze zelfstudie laten we zien hoe u dat doet elementen toevoegen, bewerken of verwijderen uit XML in Groovy met behulp van verschillende benaderingen. We zullen ook laten zien hoe maak een geheel nieuwe XML-structuur.

2. Het model definiëren

Laten we een XML-structuur definiëren in onze bronnenmap die we in onze voorbeelden zullen gebruiken:

  Eerste stappen in Java Siena Kerr 2018-12-01 Dockeriseer je SpringBoot-applicatie Jonas Lugo 2018-12-01 SpringBoot-tutorial Daniele Ferguson 2018-06-12 Java 12 inzichten Siena Kerr 2018-07-22 

En lees het in een InputStream variabele:

def xmlFile = getClass (). getResourceAsStream ("artikelen.xml")

3. XmlParser

Laten we deze stream gaan verkennen met de XmlParser klasse.

3.1. Lezing

Het lezen en ontleden van een XML-bestand is waarschijnlijk de meest gebruikelijke XML-bewerking die een ontwikkelaar zal moeten uitvoeren. De XmlParser biedt een zeer eenvoudige interface die precies daarvoor bedoeld is:

def artikelen = nieuwe XmlParser (). parse (xmlFile)

Op dit punt hebben we toegang tot de attributen en waarden van de XML-structuur met behulp van GPath-expressies.

Laten we nu een eenvoudige test uitvoeren met Spock om te controleren of onze Lidwoord object is correct:

def "Moet XML-bestand correct lezen" () {gegeven: "XML-bestand" wanneer: "XmlParser gebruiken om bestand te lezen" def artikelen = nieuwe XmlParser (). parse (xmlFile) dan: "Xml is correct geladen" artikelen. '* '.size () == 4 artikelen.article [0] .author.firstname.text () == "Siena" artikelen.artikel [2].' release-date'.text () == "2018-06- 12 "Articles.article [3] .title.text () ==" Java 12 insights "artikelen.article.find {it.author. '@ Id'.text () ==" 3 "} .author.firstname. text () == "Daniele"}

Om te begrijpen hoe u toegang kunt krijgen tot XML-waarden en hoe u de GPath-expressies kunt gebruiken, laten we ons even concentreren op de interne structuur van het resultaat van de XmlParser # ontleden operatie.

De Lidwoord object is een instantie van groovy.util.Node. Elke Knooppunt bestaat uit een naam, attributen map, waarde en ouder (die beide kunnen zijn nul of een ander Knooppunt).

In ons geval is de waarde van Lidwoord is een groovy.util.NodeList instantie, wat een wrapper-klasse is voor een verzameling Knooppunts. De NodeList breidt de java.util.ArrayList class, die extractie van elementen per index biedt. Om de tekenreekswaarde a te verkrijgen Knooppunt, we gebruiken groovy.util.Node # tekst ().

In het bovenstaande voorbeeld hebben we een paar GPath-expressies geïntroduceerd:

  • artikelen.artikel [0] .author.voornaam - zoek de voornaam van de auteur voor het eerste artikel - artikelen.artikel [n] zou rechtstreeks toegang hebben tot het nth artikel
  • ‘*' - krijg een lijst van artikel‘S kinderen - het is het equivalent van groovy.util.Node # kinderen ()
  • auteur.'@id ' - pak de schrijver element ID kaart attribuut - author.'@attributeName ' benadert de attribuutwaarde met zijn naam (de equivalenten zijn: auteur [‘@ id '] en [e-mail beveiligd])

3.2. Een knooppunt toevoegen

Laten we, net als in het vorige voorbeeld, eerst de XML-inhoud in een variabele lezen. Dit stelt ons in staat om een ​​nieuw knooppunt te definiëren en deze aan onze artikellijst toe te voegen met groovy.util.Node # toevoegen.

Laten we nu een test uitvoeren die ons punt bewijst:

def "Moet knooppunt toevoegen aan bestaande xml met NodeBuilder" () {gegeven: "XML-object" def artikelen = nieuwe XmlParser (). parse (xmlFile) wanneer: "Knooppunt toevoegen aan xml" def articleNode = nieuw NodeBuilder (). artikel ( id: '5') {title ('XML doorkruisen in de notendop') author {firstname ('Martin') lastname ('Schmidt')} 'release-date' ('2019-05-18')} artikelen.append (articleNode) dan: "Knooppunt is correct toegevoegd aan xml" artikelen. '*'. size () == 5 artikelen.article [4] .title.text () == "XML doorkruisen in het kort"}

Zoals we in het bovenstaande voorbeeld kunnen zien, is het proces vrij eenvoudig.

Laten we ook opmerken dat we groovy.util.NodeBuilder, wat een leuk alternatief is voor het gebruik van de Knooppunt constructor voor onze Knooppunt definitie.

3.3. Een knooppunt wijzigen

We kunnen ook de waarden van knooppunten wijzigen met de XmlParser. Laten we hiervoor nogmaals de inhoud van het XML-bestand ontleden. Vervolgens kunnen we het inhoudsknooppunt bewerken door de waarde veld van de Knooppunt voorwerp.

Laten we dat een tijdje onthouden XmlParser gebruikt de GPath-expressies, halen we altijd de instantie van de NodeList, dus om het eerste (en enige) element te wijzigen, moeten we het openen met behulp van zijn index.

Laten we onze aannames controleren door een snelle test te schrijven:

def "Moet knooppunt wijzigen" () {gegeven: "XML-object" def artikelen = nieuwe XmlParser (). parse (xmlFile) wanneer: "Waarde van een van de knooppunten wijzigen" Articles.article.each {it.'release-date '[0] .value = "2019-05-18"} en vervolgens: "XML is bijgewerkt" Articles.article.findAll {it.'release-date'.text ()! = "2019-05-18"}. is leeg() }

In het bovenstaande voorbeeld hebben we ook de Groovy Collections API gebruikt om het NodeList.

3.4. Een knooppunt vervangen

Laten we vervolgens kijken hoe we het hele knooppunt kunnen vervangen in plaats van alleen een van zijn waarden te wijzigen.

Net als bij het toevoegen van een nieuw element, gebruiken we de NodeBuilder voor de Knooppunt definitie en vervang vervolgens een van de bestaande knooppunten erin met groovy.util.Node # replaceNode:

def "Moet knooppunt vervangen" () {gegeven: "XML-object" def artikelen = nieuwe XmlParser (). parse (xmlFile) wanneer: "Knooppunt toevoegen aan xml" def articleNode = nieuw NodeBuilder (). artikel (id: '5' ) {title ('Traversing XML in the nutshell') author {firstname ('Martin') lastname ('Schmidt')} 'release-date' ('2019-05-18')} Articles.article [0] .replaceNode (articleNode) dan: "Knooppunt is correct aan xml toegevoegd" artikelen. '*'. size () == 4 artikelen.article [0] .title.text () == "XML in de notendop doorkruisen"}

3.5. Een knooppunt verwijderen

Een knooppunt verwijderen met de XmlParser is best lastig. Hoewel de Knooppunt klasse biedt de remove (Node kind) methode, in de meeste gevallen zouden we het niet op zichzelf gebruiken.

In plaats daarvan laten we zien hoe u een knooppunt verwijdert waarvan de waarde aan een bepaalde voorwaarde voldoet.

Standaard toegang tot de geneste elementen met behulp van een ketting van Node.NodeList referenties geeft een kopie terug van de corresponderende onderliggende knooppunten. Daarom kunnen we de java.util.NodeList # removeAll methode rechtstreeks op onze artikel verzameling.

Om een ​​knooppunt met een predikaat te verwijderen, moeten we eerst alle knooppunten vinden die aan onze voorwaarde voldoen, ze vervolgens doorlopen en aanroepen java.util.Node # verwijderen methode op de ouder elke keer.

Laten we een test implementeren die alle artikelen verwijdert waarvan de auteur een andere ID heeft dan 3:

def "Moet artikel verwijderen uit xml" () {gegeven: "XML-object" def artikelen = nieuwe XmlParser (). parse (xmlFile) wanneer: "Alle artikelen verwijderen behalve die met id == 3" Articles.article .findAll { it.author. '@ id'.text ()! = "3"}. elk {artikelen.remove (it)} dan: "Er is nog maar één artikel over" Articles.children (). size () == 1 artikelen.artikel [0] .author. '@ id'.text () == "3"}

Zoals we kunnen zien, hebben we als resultaat van onze verwijderingsoperatie een XML-structuur ontvangen met slechts één artikel, en zijn id is 3.

4. XmlSlurper

Groovy biedt ook een andere klasse die zich toelegt op het werken met XML. In deze sectie laten we zien hoe u de XML-structuur kunt lezen en manipuleren met behulp van de XmlSlurper.

4.1. Lezing

Laten we, net als in onze vorige voorbeelden, beginnen met het ontleden van de XML-structuur vanuit een bestand:

def "Moet XML-bestand correct lezen" () {gegeven: "XML-bestand" wanneer: "XmlSlurper gebruiken om bestand te lezen" def artikels = nieuwe XmlSlurper (). parse (xmlFile) dan: "Xml is correct geladen" artikelen. '* '.size () == 4 artikelen.artikel [0] .author.firstname == "Siena" artikelen.artikel [2].' release-date '== "2018-06-12" artikelen.artikel [3] .title == "Java 12 insights" artikelen.article.find {it.author.'@id '== "3"} .author.firstname == "Daniele"}

Zoals we kunnen zien, is de interface identiek aan die van XmlParser. De uitvoerstructuur gebruikt echter de groovy.util.slurpersupport.GPathResult, dat is een wrapper-klasse voor Knooppunt. GPathResult biedt vereenvoudigde definities van methoden zoals: is gelijk aan () en toString () door inpakken Knooppunt # tekst (). Als gevolg hiervan kunnen we velden en parameters rechtstreeks lezen met alleen hun namen.

4.2. Een knooppunt toevoegen

Een Knooppunt lijkt ook erg op het gebruik van XmlParser. In dit geval echter groovy.util.slurpersupport.GPathResult # appendNode biedt een methode die een instantie van java.lang.Object als argument. Hierdoor kunnen we nieuw vereenvoudigen Knooppunt definities volgens dezelfde conventie geïntroduceerd door KnooppuntBouwer:

def "Moet knooppunt toevoegen aan bestaande xml" () {gegeven: "XML-object" def artikelen = nieuwe XmlSlurper (). parse (xmlFile) wanneer: "Knooppunt toevoegen aan xml" artikelen.appendNode {artikel (id: '5') {title ('Traversing XML in the nutshell') author {firstname ('Martin') lastname ('Schmidt')} 'release-date' ('2019-05-18')}} artikelen = nieuwe XmlSlurper (). parseText (XmlUtil.serialize (artikelen)) en vervolgens: "Knooppunt is correct aan xml toegevoegd" artikelen. '*'. Size () == 5 artikelen.artikel [4] .title == "XML in een notendop doorkruisen"}

In het geval dat we de structuur van onze XML moeten wijzigen met XmlSlurper, we moeten onze Lidwoord bezwaar maken om de resultaten te zien. Dat kunnen we bereiken door de combinatie van de groovy.util.XmlSlurper # parseText en de groovy.xmlXmlUtil # serialize methoden.

4.3. Een knooppunt wijzigen

Zoals we eerder al zeiden, de GPathResult introduceert een vereenvoudigde benadering van gegevensmanipulatie. Dat gezegd hebbende, in tegenstelling tot de XmlSlurper, we kunnen de waarden rechtstreeks wijzigen met behulp van de knooppuntnaam of parameternaam:

def "Moet knooppunt wijzigen" () {gegeven: "XML-object" def artikelen = nieuwe XmlSlurper (). parse (xmlFile) wanneer: "Waarde van een van de knooppunten wijzigen" Articles.article.each {it.'release-date '= "2019-05-18"} en vervolgens: "XML is bijgewerkt" Articles.article.findAll {it.'release-date'! = "2019-05-18"} .isEmpty ()}

Laten we opmerken dat als we alleen de waarden van het XML-object wijzigen, we niet de hele structuur opnieuw hoeven te ontleden.

4.4. Een knooppunt vervangen

Laten we nu overgaan tot het vervangen van het hele knooppunt. Nogmaals, de GPathResult komt te hulp. We kunnen het knooppunt eenvoudig vervangen met groovy.util.slurpersupport.NodeChild # replaceNode, die zich uitstrekt GPathResult en volgt dezelfde conventie voor het gebruik van de Voorwerp waarden als argumenten:

def "Moet knooppunt vervangen" () {gegeven: "XML-object" def artikelen = nieuwe XmlSlurper (). parse (xmlFile) wanneer: "Knooppunt vervangen" artikelen.article [0] .replaceNode {artikel (id: '5') {title ('Traversing XML in the nutshell') author {firstname ('Martin') lastname ('Schmidt')} 'release-date' ('2019-05-18')}} artikelen = nieuwe XmlSlurper (). parseText (XmlUtil.serialize (artikelen)) dan: "Knooppunt is correct vervangen" artikelen. '*'. Size () == 4 artikelen.artikel [0] .title == "XML in het kort"}

Zoals het geval was bij het toevoegen van een knooppunt, wijzigen we de structuur van de XML, dus we moeten het opnieuw ontleden.

4.5. Een knooppunt verwijderen

Om een ​​knooppunt te verwijderen met XmlSlurper, we kunnen de groovy.util.slurpersupport.NodeChild # replaceNode methode door simpelweg een leeg Knooppunt definitie:

def "Moet artikel verwijderen uit xml" () {gegeven: "XML-object" def artikelen = nieuwe XmlSlurper (). parse (xmlFile) wanneer: "Alle artikelen verwijderen behalve die met id == 3" Articles.article .findAll { it.author.'@id '! = "3"} .replaceNode {} artikelen = nieuwe XmlSlurper (). parseText (XmlUtil.serialize (artikelen)) en vervolgens: "Er is nog maar één artikel over" Articles.children (). size () == 1 artikelen.artikel [0]. auteur. '@ id' == "3"}

Nogmaals, het wijzigen van de XML-structuur vereist een herinitialisatie van onze Lidwoord voorwerp.

5. XmlParser vs XmlSlurper

Zoals we in onze voorbeelden hebben laten zien, is het gebruik van XmlParser en XmlSlurper zijn vrij gelijkaardig. Met beide kunnen we min of meer dezelfde resultaten behalen. Sommige verschillen tussen hen kunnen de weegschaal echter naar de een of de ander kantelen.

Allereerst,XmlParser parseert altijd het hele document in de DOM-achtige structuur. Daardoor kunnen we er tegelijkertijd uit lezen en erin schrijven. We kunnen niet hetzelfde doen met XmlSlurper omdat het paden lui evalueert. Als resultaat, XmlParser kan meer geheugen verbruiken.

Aan de andere kant, XmlSlurper gebruikt eenvoudiger definities, waardoor het eenvoudiger is om mee te werken. Dat moeten we ook onthouden eventuele structurele wijzigingen in XML met behulp van XmlSlurper vereisen herinitialisatie, wat een onaanvaardbare prestatiehit kan hebben in het geval van veel wijzigingen achter elkaar.

De beslissing welke tool moet worden gebruikt, moet met zorg worden gemaakt en hangt volledig af van de use case.

6. MarkupBuilder

Naast het lezen en manipuleren van de XML-boom, biedt Groovy ook tools om een ​​volledig nieuw XML-document te maken. Laten we nu een document maken dat bestaat uit de eerste twee artikelen uit ons eerste voorbeeld met groovy.xml.MarkupBuilder:

def "Moet XML correct maken" () {gegeven: "Knooppuntstructuren" wanneer: "MarkupBuilderTest gebruiken om XML-structuur te maken" def writer = new StringWriter () nieuwe MarkupBuilder (schrijver) .articles {article {title ('Eerste stappen in Java ') author (id:' 1 ') {firstname (' Siena ') lastname (' Kerr ')}' release-date '(' 2018-12-01 ')} article {title (' Dockerize your SpringBoot application ') author (id: '2') {voornaam ('Jonas') achternaam ('Lugo')} 'release-date' ('2018-12-01')}} dan: "Xml is correct aangemaakt" XmlUtil.serialize ( writer.toString ()) == XmlUtil.serialize (xmlFile.text)}

In het bovenstaande voorbeeld kunnen we dat zien MarkupBuilder gebruikt dezelfde aanpak voor de Knooppunt definities die we hebben gebruikt NodeBuilder en GPathResult eerder.

Om output van te vergelijken MarkupBuilder met de verwachte XML-structuur hebben we de groovy.xml.XmlUtil # serialize methode.

7. Conclusie

In dit artikel hebben we verschillende manieren onderzocht om XML-structuren te manipuleren met Groovy.

We hebben voorbeelden bekeken van het parseren, toevoegen, bewerken, vervangen en verwijderen van knooppunten met behulp van twee klassen die door Groovy worden geleverd: XmlParser en XmlSlurper. We bespraken ook de verschillen tussen hen en lieten zien hoe we met MarkupBuilder.

Zoals altijd is de volledige code die in dit artikel wordt gebruikt, beschikbaar op GitHub.


$config[zx-auto] not found$config[zx-overlay] not found