Introduktion til XPath med Java

1. Oversigt

I denne artikel vil vi gå over det grundlæggende i XPath med understøttelse i standard Java JDK.

Vi skal bruge et simpelt XML-dokument, behandle det og se, hvordan man går gennem dokumentet for at udtrække de oplysninger, vi har brug for fra det.

XPath er en standardsyntaks, der anbefales af W3C, det er et sæt udtryk for at navigere i XML-dokumenter. Du kan finde en fuld XPath-reference her.

2. En simpel XPath-parser

importere javax.xml.namespace.NamespaceContext; importere javax.xml.parsers.DocumentBuilder; importere javax.xml.parsers.DocumentBuilderFactory; importere javax.xml.parsers.ParserConfigurationException; importere javax.xml.xpath.XPath; importere javax.xml.xpath.XPathConstants; importere javax.xml.xpath.XPathExpressionException; importere javax.xml.xpath.XPathFactory; import org.w3c.dom.Document; offentlig klasse DefaultParser {privat filfil; offentlig DefaultParser (filfil) {this.file = fil; }} 

Lad os nu se nærmere på de elementer, du finder i StandardParser:

FileInputStream fileIS = ny FileInputStream (this.getFile ()); DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance (); DocumentBuilder builder = builderFactory.newDocumentBuilder (); Dokument xmlDocument = builder.parse (fileIS); XPath xPath = XPathFactory.newInstance (). NewXPath (); String expression = "/ Tutorials / Tutorial"; nodeList = (NodeList) xPath.compile (expression). evaluere (xmlDocument, XPathConstants.NODESET);

Lad os nedbryde det:

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance ();

Vi bruger dette objekt til at fremstille et DOM-objekttræ fra vores xml-dokument:

DocumentBuilder builder = builderFactory.newDocumentBuilder ();

Efter at have en forekomst af denne klasse kan vi analysere XML-dokumenter fra mange forskellige inputkilder som f.eks InputStream, Fil, URL og SAX:

Dokument xmlDocument = builder.parse (fileIS);

EN Dokument (org.w3c.dom.Document) repræsenterer hele XML-dokumentet, er roden til dokumenttræet, giver vores første adgang til data:

XPath xPath = XPathFactory.newInstance (). NewXPath ();

Fra XPath-objektet får vi adgang til udtrykkene og udfører dem over vores dokument for at udtrække det, vi har brug for fra det:

xPath.compile (expression) .evaluate (xmlDocument, XPathConstants.NODESET);

Vi kan kompilere et XPath-udtryk, der er sendt som streng, og definere, hvilken type data vi forventer at modtage en sådan NODESET, NODE eller Snor for eksempel.

3. Lad os starte

Nu hvor vi kiggede på de basiskomponenter, vi vil bruge, kan vi starte med noget kode ved hjælp af nogle enkle XML til testformål:

   Guava Introduktion til Guava 04/04/2016 GuavaAuthor XML Introduktion til XPath 04/05/2016 XMLAuthor 

3.1. Hent en grundlæggende liste over elementer

Den første metode er en simpel brug af et XPath-udtryk til at hente en liste over noder fra XML:

FileInputStream fileIS = ny FileInputStream (this.getFile ()); DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance (); DocumentBuilder builder = builderFactory.newDocumentBuilder (); Dokument xmlDocument = builder.parse (fileIS); XPath xPath = XPathFactory.newInstance (). NewXPath (); String expression = "/ Tutorials / Tutorial"; nodeList = (NodeList) xPath.compile (expression). evaluere (xmlDocument, XPathConstants.NODESET); 

Vi kan hente tutorial-listen indeholdt i rodnoden ved at bruge udtrykket ovenfor eller ved at bruge udtrykket “// Vejledning”Men denne vil hente alt noder i dokumentet fra den aktuelle node, uanset hvor de er placeret i dokumentet, betyder det uanset hvilket niveau af træet der starter fra den aktuelle node.

Det NodeListe det vender tilbage ved at specificere NODESET til kompileringsinstruktionen som returtype er en ordnet samling af noder, der kan tilgås ved at sende et indeks som parameter.

3.2. Henter en bestemt node efter dens id

Vi kan se efter et element baseret på et givet id bare ved at filtrere:

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance (); DocumentBuilder builder = builderFactory.newDocumentBuilder (); Dokument xmlDocument = builder.parse (this.getFile ()); XPath xPath = XPathFactory.newInstance (). NewXPath (); String expression = "/ Tutorials / Tutorial [@ tutId =" + "'" + id + "'" + "]"; node = (Node) xPath.compile (udtryk). evaluere (xmlDocument, XPathConstants.NODE); 

Ved at bruge denne form for udtryk kan vi filtrere efter det element, vi har brug for at se efter, bare ved at bruge den korrekte syntaks. Disse slags udtryk kaldes predikater, og de er en nem måde at lokalisere specifikke data over et dokument på, for eksempel:

/ Tutorials / Tutorial [1]

/ Tutorials / Tutorial [first ()]

/ Tutorials / Tutorial [position () <4]

Du kan finde en komplet reference af prædikater her

3.3. Henter noder efter et specifikt tagnavn

Nu går vi videre ved at introducere akser, lad os se, hvordan dette fungerer ved at bruge det i et XPath-udtryk:

Dokument xmlDocument = builder.parse (this.getFile ()); this.clean (xmlDocument); XPath xPath = XPathFactory.newInstance (). NewXPath (); String expression = "// Tutorial [descendant :: title [text () =" + "'" + name + "'" + "]]"; nodeList = (NodeList) xPath.compile (expression). evaluere (xmlDocument, XPathConstants.NODESET); 

Med det ovenfor anvendte udtryk leder vi efter hvert element, der har en efterkommer med teksten sendt som parameter i variablen "navn".

Efter den xml-prøve, der er angivet i denne artikel, kunne vi se efter en indeholdende teksten “Guava” eller “XML”, og vi henter det hele element med alle dets data.

Akser giver en meget fleksibel måde at navigere i et XML-dokument, og du kan finde en fuld dokumentation på det officielle websted.

3.4. Manipulering af data i udtryk

XPath giver os mulighed for også at manipulere data i udtrykkene, hvis det er nødvendigt.

XPath xPath = XPathFactory.newInstance (). NewXPath (); String expression = "// Tutorial [number (translate (date, '/', ''))>>" + date + "]"; nodeList = (NodeList) xPath.compile (expression). evaluere (xmlDocument, XPathConstants.NODESET); 

I dette udtryk overfører vi til vores metode en simpel streng som en dato, der ligner "ddmmyyyy", men XML gemmer disse data med formatet "dd / mm / åååå“Så for at matche et resultat manipulerer vi strengen for at konvertere den til det korrekte dataformat, der bruges i vores dokument, og vi gør det ved hjælp af en af ​​funktionerne leveret af XPath

3.5. Henter elementer fra et dokument med defineret navneområde

Hvis vores xml-dokument har et navneområde defineret som det er i eksempel_namespace.xml, der bruges her, ændres reglerne for at hente de data, vi har brug for, da vores xml starter sådan:

Når vi nu bruger et udtryk svarende til “// Tutorial ”, vi får ikke noget resultat. Dette XPath-udtryk vil returnere alle elementer, der ikke er under noget navneområde, og i vores nye eksempel_namespace.xml alle elementer er defineret i navneområdet / fuld_arkiv.

Lad os se, hvordan vi håndterer navneområder.

Først og fremmest skal vi indstille navneområdet, så XPath kan vide, hvor vi leder efter vores data:

xPath.setNamespaceContext (ny NamespaceContext () {@ Override offentlig Iterator getPrefixes (String arg0) {return null;} @ Override public String getPrefix (String arg0) {return null;} @ Override public String getNamespaceURI (String arg0) {if (" bdn ".equals (arg0)) {return" / full_archive ";} return null;}}); 

I metoden ovenfor definerer vi “bdn”Som navnet på vores navneområde“/ fuld_arkiv“, Og fra nu af skal vi tilføje“bdn”Til XPath-udtryk, der bruges til at lokalisere elementer:

String expression = "/ bdn: Tutorials / bdn: Tutorial"; nodeList = (NodeList) xPath.compile (expression). evaluere (xmlDocument, XPathConstants.NODESET); 

Ved hjælp af ovenstående udtryk er vi i stand til at hente alt elementer under “bdn”Navneområde.

3.6. Undgå tomme tekstknudeproblemer

Som du kunne bemærke, kaldes en ny funktion i koden i afsnit 3.3 i denne artikel lige efter at have analyseret vores XML til et dokumentobjekt, dette .clean (xmlDocument);

Nogle gange når vi gentager gennem elementer, underordnede knuder og så videre, hvis vores dokument har tomme tekstknudepunkter, kan vi finde en uventet adfærd i de resultater, vi ønsker at få.

Vi ringede node .getFirstChild () når vi gentager alt sammen elementer på udkig efter information, men i stedet for det, vi leder efter, har vi bare "#Text" som en tom knude.

For at løse problemet kan vi navigere gennem vores dokument og fjerne de tomme noder, som denne:

NodeList childs = node.getChildNodes (); for (int n = childs.getLength () - 1; n> = 0; n--) {Node barn = childs.item (n); kort nodeType = child.getNodeType (); hvis (nodeType == Node.ELEMENT_NODE) ​​{ren (barn); } ellers hvis (nodeType == Node.TEXT_NODE) ​​{String trimmedNodeVal = child.getNodeValue (). trim (); hvis (trimmedNodeVal.length () == 0) {node.removeChild (barn); } andet {child.setNodeValue (trimmetNodeVal); }} ellers hvis (nodeType == Node.COMMENT_NODE) ​​{node.removeChild (underordnet); }}

Ved at gøre dette kan vi kontrollere hver type node, vi finder, og fjerne dem, vi ikke har brug for.

4 konklusioner

Her introducerede vi netop den understøttede XPath-standard, men der er mange populære biblioteker som JDOM, Saxon, XQuery, JAXP, Jaxen eller endda Jackson nu. Der er også biblioteker til specifik HTML-parsing som JSoup.

Det er ikke begrænset til java, XPath-udtryk kan bruges af XSLT-sproget til at navigere i XML-dokumenter.

Som du kan se, er der en lang række muligheder for, hvordan man håndterer denne slags filer.

Der er en god standardunderstøttelse som standard til XML / HTML-dokumenter parsing, læsning og behandling. Du kan finde den fulde arbejdseksempel her.