Introduksjon til XPath med Java

1. Oversikt

I denne artikkelen skal vi gå over det grunnleggende om XPath med støtte i standard Java JDK.

Vi skal bruke et enkelt XML-dokument, behandle det og se hvordan vi går gjennom dokumentet for å hente ut informasjonen vi trenger fra det.

XPath er en standardsyntaks anbefalt av W3C, det er et sett med uttrykk for å navigere i XML-dokumenter. Du finner en fullstendig XPath-referanse her.

2. En enkel 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; importer org.w3c.dom.Document; offentlig klasse DefaultParser {privat filfil; offentlig DefaultParser (filfil) {this.file = fil; }} 

La oss nå se nærmere på elementene du finner i DefaultParser:

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

La oss bryte ned det:

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance ();

Vi vil bruke dette objektet til å produsere et DOM-objekt tre fra vårt xml-dokument:

DocumentBuilder builder = builderFactory.newDocumentBuilder ();

Etter å ha en forekomst av denne klassen, kan vi analysere XML-dokumenter fra mange forskjellige inndatakilder som InputStream, Fil, URL og SAX:

Dokument xmlDocument = builder.parse (fileIS);

EN Dokument (org.w3c.dom.Document) representerer hele XML-dokumentet, er roten til dokumenttreet, gir vår første tilgang til data:

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

Fra XPath-objektet får vi tilgang til uttrykkene og utfører dem over dokumentet vårt for å trekke ut det vi trenger fra det:

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

Vi kan kompilere et XPath-uttrykk sendt som streng og definere hva slags data vi forventer å motta et slikt NODESETT, NODE eller String for eksempel.

3. La oss starte

Nå som vi tok en titt på basiskomponentene vi skal bruke, kan vi starte med litt kode ved hjelp av noen enkle XML, for testformål:

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

3.1. Hent en grunnleggende liste over elementer

Den første metoden er en enkel bruk av et XPath-uttrykk for å 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) .evaluate (xmlDocument, XPathConstants.NODESET); 

Vi kan hente opplæringslisten i rotnoden ved å bruke uttrykket ovenfor, eller ved å bruke uttrykket “//Opplæringen”Men denne vil hente alt noder i dokumentet fra gjeldende node uansett hvor de er plassert i dokumentet, betyr dette uansett hvilket nivå av treet som starter fra den nåværende noden.

De NodeList den returnerer ved å spesifisere NODESETT til kompileringsinstruksjonen som returtype, er en ordnet samling av noder som kan nås ved å sende en indeks som parameter.

3.2. Henter en spesifikk node etter ID

Vi kan se etter et element basert på en gitt ID bare ved å 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 (expression) .evaluate (xmlDocument, XPathConstants.NODE); 

Ved å bruke denne typen uttrykk kan vi filtrere etter hvilket element vi trenger å se etter bare ved å bruke riktig syntaks. Denne typen uttrykk kalles predikater, og de er en enkel måte å finne spesifikke data over et dokument, for eksempel:

/ Opplæring / Opplæring [1]

/ Opplæring / Opplæring [første ()]

/ Opplæring / Opplæring [posisjon () <4]

Du kan finne en fullstendig referanse til predikater her

3.3. Henter noder etter et spesifikt tagnavn

Nå går vi videre ved å introdusere akser, la oss se hvordan dette fungerer ved å bruke det i et XPath-uttrykk:

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) .evaluate (xmlDocument, XPathConstants.NODESET); 

Med uttrykket brukt ovenfor ser vi etter hvert element som har en etterkommer med teksten sendt som parameter i "navn" -variabelen.

Etter xml-eksemplet som er gitt for denne artikkelen, kan vi se etter en som inneholder teksten “Guava” eller “XML”, og vi vil hente ut helheten element med alle dataene.

Axes gir en veldig fleksibel måte å navigere i et XML-dokument, og du kan finne full dokumentasjon på det offisielle nettstedet.

3.4. Manipulere data i uttrykk

XPath lar oss også manipulere data i uttrykkene om nødvendig.

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

I dette uttrykket overfører vi til vår metode en enkel streng som en dato som ser ut som "ddmmyyyy", men XML lagrer disse dataene med formatet "dd / mm / åååå“, Så for å matche et resultat manipulerer vi strengen for å konvertere den til riktig dataformat som brukes av dokumentet vårt, og vi gjør det ved å bruke en av funksjonene som tilbys av XPath

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

Hvis xml-dokumentet vårt har et navneområde definert slik det er i eksempel_namespace.xml som brukes her, vil reglene for å hente dataene vi trenger endres siden xml-en vår starter slik:

Nå når vi bruker et uttrykk som ligner på “// Tutorial ”, vi kommer ikke til å få noe resultat. Det XPath-uttrykket kommer til å returnere alt elementer som ikke er under noe navneområde, og i vårt nye eksempel_namespace.xml, alt elementer er definert i navneområdet / fullarkiv.

Lar oss se hvordan vi skal håndtere navneområder.

Først og fremst trenger vi å stille inn konteksten i navneområdet, slik at XPath kan vite hvor vi leter etter dataene våre:

xPath.setNamespaceContext (new NamespaceContext () {@ Override public 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å vårt navneområde“/ fullarkiv", Og fra nå av må vi legge til"bdn”Til XPath-uttrykkene som brukes til å lokalisere elementer:

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

Ved å bruke uttrykket ovenfor er vi i stand til å hente ut alt elementer under “bdn”Navneområdet.

3.6. Unngå problemer med tomme tekstnoder

Som du kunne legge merke til, kalles en ny funksjon i koden i 3.3-delen av denne artikkelen rett etter å ha analysert XML til et dokumentobjekt, dette .clean (xmlDocument);

Noen ganger når vi gjentar oss gjennom elementer, barnekoder og så videre, hvis dokumentet vårt har tomme tekstnoder, kan vi finne en uventet oppførsel i resultatene vi ønsker å få.

Vi ringte node .getFirstChild () når vi itererer over alt elementer på jakt etter informasjon, men i stedet for det vi leter etter, har vi bare “#Text” som en tom node.

For å fikse problemet kan vi navigere gjennom dokumentet vårt og fjerne de tomme nodene, slik:

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

Ved å gjøre dette kan vi sjekke hver type node vi finner og fjerne de vi ikke trenger.

4. Konklusjoner

Her introduserte vi nettopp standard XPath-støtte, men det er mange populære biblioteker som JDOM, Saxon, XQuery, JAXP, Jaxen eller til og med Jackson nå. Det er også biblioteker for spesifikk HTML-parsing som JSoup.

Det er ikke begrenset til java, XPath-uttrykk kan brukes av XSLT-språk for å navigere i XML-dokumenter.

Som du kan se, er det et bredt spekter av muligheter for hvordan du håndterer denne typen filer.

Det er en god standardstøtte som standard for XML / HTML-dokumenter parsing, lesing og behandling. Du kan finne hele arbeidsprøven her.