Jak wypisać bieżącą ścieżkę elementu w XSLT?
W XSLT, czy istnieje sposób, aby określić, gdzie jesteś w dokumencie XML podczas przetwarzania elementu?
Przykład: podano następujący Fragment XML Doc...
<Doc>
<Ele1>
<Ele11>
<Ele111>
</Ele111>
</Ele11>
</Ele1>
<Ele2>
</Ele2>
</Doc>
W XSLT, jeśli mój kontekst jest elementem "Ele111", Jak mogę zmusić XSLT do wypisania pełnej ścieżki? Chciałbym, aby wyszło: "/Doc/Ele1/Ele11 / Ele111".
Kontekst tego pytania: mam bardzo duży, bardzo głęboki dokument, który chcę przejrzeć wyczerpująco( ogólnie używając rekurencji) i jeśli znajdę element z określonym atrybutem, chcę wiedzieć, gdzie go znalazłem. Przypuszczam, że mógłbym nieść moją obecną ścieżkę podczas trawersowania, ale myślę, że XSLT / XPath powinien wiedzieć.
5 answers
Nie myśl, że jest to wbudowane w XPath, prawdopodobnie potrzebujesz rekurencyjnego szablonu, takiego jak ten TUTAJ , na którym oparłem ten przykład. Spowoduje to przejście każdego elementu w dokumencie XML i wyświetli ścieżkę do tego elementu w stylu podobnym do opisanego przez Ciebie.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:template match="/">
<paths>
<xsl:apply-templates/>
</paths>
</xsl:template>
<xsl:template match="//*">
<path>
<xsl:for-each select="ancestor-or-self::*">
<xsl:call-template name="print-step"/>
</xsl:for-each>
</path>
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template name="print-step">
<xsl:text>/</xsl:text>
<xsl:value-of select="name()"/>
<xsl:text>[</xsl:text>
<xsl:value-of select="1+count(preceding-sibling::*)"/>
<xsl:text>]</xsl:text>
</xsl:template>
</xsl:stylesheet>
Jest kilka komplikacji; rozważ to drzewo:
<root>
<child/>
<child/>
</root>
Jak odróżnić dwa węzły potomne? Więc potrzebujesz indeksu do swojej sekwencji elementów, dziecko1 oraz na przykład dziecko [2].
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-04 21:44:57
Aktualnie zaakceptowana odpowiedź zwróci niepoprawne ścieżki. Na przykład element Ele2
W op sample XML zwróci ścieżkę /Doc[1]/Ele2[2]
. Powinno być /Doc[1]/Ele2[1]
.
Oto podobny szablon XSLT 1.0, który zwraca poprawne ścieżki:
<xsl:template name="genPath">
<xsl:param name="prevPath"/>
<xsl:variable name="currPath" select="concat('/',name(),'[',
count(preceding-sibling::*[name() = name(current())])+1,']',$prevPath)"/>
<xsl:for-each select="parent::*">
<xsl:call-template name="genPath">
<xsl:with-param name="prevPath" select="$currPath"/>
</xsl:call-template>
</xsl:for-each>
<xsl:if test="not(parent::*)">
<xsl:value-of select="$currPath"/>
</xsl:if>
</xsl:template>
Oto przykład, który doda atrybut {[9] } do wszystkich elementów.
Wejście XML
<Doc>
<Ele1>
<Ele11>
<Ele111>
<foo/>
<foo/>
<bar/>
<foo/>
<foo/>
<bar/>
<bar/>
</Ele111>
</Ele11>
</Ele1>
<Ele2/>
</Doc>
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="text()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:attribute name="path">
<xsl:call-template name="genPath"/>
</xsl:attribute>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template name="genPath">
<xsl:param name="prevPath"/>
<xsl:variable name="currPath" select="concat('/',name(),'[',
count(preceding-sibling::*[name() = name(current())])+1,']',$prevPath)"/>
<xsl:for-each select="parent::*">
<xsl:call-template name="genPath">
<xsl:with-param name="prevPath" select="$currPath"/>
</xsl:call-template>
</xsl:for-each>
<xsl:if test="not(parent::*)">
<xsl:value-of select="$currPath"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Wyjście XML
<Doc path="/Doc[1]">
<Ele1 path="/Doc[1]/Ele1[1]">
<Ele11 path="/Doc[1]/Ele1[1]/Ele11[1]">
<Ele111 path="/Doc[1]/Ele1[1]/Ele11[1]/Ele111[1]">
<foo path="/Doc[1]/Ele1[1]/Ele11[1]/Ele111[1]/foo[1]"/>
<foo path="/Doc[1]/Ele1[1]/Ele11[1]/Ele111[1]/foo[2]"/>
<bar path="/Doc[1]/Ele1[1]/Ele11[1]/Ele111[1]/bar[1]"/>
<foo path="/Doc[1]/Ele1[1]/Ele11[1]/Ele111[1]/foo[3]"/>
<foo path="/Doc[1]/Ele1[1]/Ele11[1]/Ele111[1]/foo[4]"/>
<bar path="/Doc[1]/Ele1[1]/Ele11[1]/Ele111[1]/bar[2]"/>
<bar path="/Doc[1]/Ele1[1]/Ele11[1]/Ele111[1]/bar[3]"/>
</Ele111>
</Ele11>
</Ele1>
<Ele2 path="/Doc[1]/Ele2[1]"/>
</Doc>
Oto kolejna wersja to wyświetla predykat pozycyjny tylko wtedy, gdy jest to potrzebne. Ten przykład jest również inny, ponieważ po prostu wypisuje ścieżkę zamiast dodawać atrybut.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="text()"/>
<xsl:template match="*">
<xsl:for-each select="ancestor-or-self::*">
<xsl:value-of select="concat('/',local-name())"/>
<!--Predicate is only output when needed.-->
<xsl:if test="(preceding-sibling::*|following-sibling::*)[local-name()=local-name(current())]">
<xsl:value-of select="concat('[',count(preceding-sibling::*[local-name()=local-name(current())])+1,']')"/>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:apply-templates select="node()"/>
</xsl:template>
</xsl:stylesheet>
Używając powyższego wejścia, arkusz stylów wyświetla:
/Doc
/Doc/Ele1
/Doc/Ele1/Ele11
/Doc/Ele1/Ele11/Ele111
/Doc/Ele1/Ele11/Ele111/foo[1]
/Doc/Ele1/Ele11/Ele111/foo[2]
/Doc/Ele1/Ele11/Ele111/bar[1]
/Doc/Ele1/Ele11/Ele111/foo[3]
/Doc/Ele1/Ele11/Ele111/foo[4]
/Doc/Ele1/Ele11/Ele111/bar[2]
/Doc/Ele1/Ele11/Ele111/bar[3]
/Doc/Ele2
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-15 19:08:20
Nie jestem pewien, którego procesora XSLT używasz, ale jeśli jest używany, możesz użyć funkcji rozszerzenia path () . Inne procesory mogą mieć taką samą funkcjonalność.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-11 09:44:06
Możesz użyć przodka XPath Axes , aby przejść wszystkich rodziców i dziadków.
<xsl:for-each select="ancestor::*">...
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-29 14:02:38
Od wersji XPath 3.0 obsługiwanej przez Saxon 9.8 (wszystkie edycje) lub Saxon 9.7 z version="3.0"
w XSLT i XmlPrime 4 (używając --xt30
), a także wydaniach Altova 2017 (używając arkuszy stylów version="3.0"
) wbudowana jest funkcja path
( https://www.w3.org/TR/xpath-functions-30/#func-path, https://www.w3.org/TR/xpath-functions-31/#func-path ), które dla wejścia jak
<?xml version="1.0" encoding="UTF-8"?>
<Doc>
<Ele1>
<Ele11>
<Ele111>
<foo/>
<foo/>
<bar/>
<foo/>
<foo/>
<bar/>
<bar/>
</Ele111>
</Ele11>
</Ele1>
<Ele2/>
</Doc>
I arkusz stylów jak
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
exclude-result-prefixes="xs math"
version="3.0">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:value-of select="//*/path()" separator=" "/>
</xsl:template>
</xsl:stylesheet>
Daje wyjście
/Q{}Doc[1]
/Q{}Doc[1]/Q{}Ele1[1]
/Q{}Doc[1]/Q{}Ele1[1]/Q{}Ele11[1]
/Q{}Doc[1]/Q{}Ele1[1]/Q{}Ele11[1]/Q{}Ele111[1]
/Q{}Doc[1]/Q{}Ele1[1]/Q{}Ele11[1]/Q{}Ele111[1]/Q{}foo[1]
/Q{}Doc[1]/Q{}Ele1[1]/Q{}Ele11[1]/Q{}Ele111[1]/Q{}foo[2]
/Q{}Doc[1]/Q{}Ele1[1]/Q{}Ele11[1]/Q{}Ele111[1]/Q{}bar[1]
/Q{}Doc[1]/Q{}Ele1[1]/Q{}Ele11[1]/Q{}Ele111[1]/Q{}foo[3]
/Q{}Doc[1]/Q{}Ele1[1]/Q{}Ele11[1]/Q{}Ele111[1]/Q{}foo[4]
/Q{}Doc[1]/Q{}Ele1[1]/Q{}Ele11[1]/Q{}Ele111[1]/Q{}bar[2]
/Q{}Doc[1]/Q{}Ele1[1]/Q{}Ele11[1]/Q{}Ele111[1]/Q{}bar[3]
/Q{}Doc[1]/Q{}Ele2[1]
Że wyjście nie jest jak Kompaktowy w przypadku braku przestrzeni nazw, jak większość ręcznie robionych prób, ale format ma tę zaletę (przynajmniej biorąc pod uwagę obsługę XPath 3.0 lub 3.1), aby umożliwić korzystanie z przestrzeni nazw i uzyskać format zwracanej ścieżki, który nie wymaga od użytkownika wyrażenia path skonfigurowania żadnych powiązań przestrzeni nazw w celu jej oceny.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-29 16:28:39