У меня есть xml-файл с такой структурой:
<?xml version = "1.1" encoding = "UTF-8" standalone = "no"?>
<databaseChangeLog xmlns = "http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd">
<changeSet author = "system" id = "65D7AFA9-17F1-4406-997E-D2B42A0E9008" dbms = "oracle">
<sql>GRANT SELECT,INSERT,UPDATE,DELETE ON ${dbSchema}.CATALOG TO ${appUser}</sql>
<rollback>REVOKE SELECT,INSERT,UPDATE,DELETE ON ${dbSchema}.CATALOG FROM ${appUser}</rollback>
</changeSet>
<changeSet author = "system" id = "E2731435-DCEE-4B7E-BA60-20A87E75227A" dbms = "oracle">
<sql>GRANT SELECT,INSERT,UPDATE,DELETE ON ${dbSchema}.CATALOGATTRIBUTE TO ${appUser}</sql>
<rollback>REVOKE SELECT,INSERT,UPDATE,DELETE ON ${dbSchema}.CATALOGATTRIBUTE FROM ${appUser}</rollback>
</changeSet>
<changeSet author = "system" id = "65D7AFA9-17F1-4406-997E-D2B42A0E9008" dbms = "oracle">
<createTable ...>
</changeSet>
</databaseChangeLog>
Я хотел бы вывести все changeSet
, где sql содержит один из tables
, в один документ, а все остальные (которые не содержат имя таблицы) в другой документ. Я думал о том, чтобы добавить что-то к переменной и в конце напечатать две переменные, но это не работает (или я не знаю, как обрабатывать дальше).
<xsl:transform version = "2.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace = "http://www.liquibase.org/xml/ns/dbchangelog"
xmlns = "http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:uuid = "http://uuid.util.java"
exclude-result-prefixes = "uuid">
<xsl:output method = "xml" indent = "yes" omit-xml-declaration = "yes"/>
<xsl:variable name = "tables"
select = "('CATALOG','CATALOGATTRIBUTE','EVENTTYPES')"/>
<xsl:template match = "node()|@*">
<xsl:copy>
<xsl:apply-templates select = "node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match = "changeSet[sql]">
<xsl:variable name = "changeSet" select = "."/>
<xsl:variable name = "sql" select = "sql"/>
<xsl:for-each select = "$tables">
<!--<xsl:variable name = "selected" select = "sql[contains(text(),concat('${dbSchema}.', ., ' '))]"/>-->
<xsl:variable name = "tableName">
<xsl:value-of select = "."/>
<xsl:value-of select = "' '"/>
</xsl:variable>
<xsl:choose>
<xsl:when test = "contains($sql, $tableName)">
<xsl:copy-of select = "$changeSet"/>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:transform>
можно как-то собрать все changeSet
которые совпадают tables
а потом все changeSets
которые не совпадают?
ps: я использую Saxon-HE-9.8.0-14 для обработки.
Спасибо
Мартин, я пытался поместить ваш сопоставитель в шаблон, но он жалуется Variable sql has not been declared (or its declaration is not in scope)
. Я хотел бы иметь оба результата, если это возможно (может быть, разделить их на файлы?).
Извините, я как-то поставил доллар перед sql
, должен был быть //changeSet[sql[every $table in $tables satisfies contains(., $table)]]
.
хм, может быть, я делаю что-то не так, но, возможно, печатает весь документ без применения шаблона. Можете ли вы опубликовать пример? Я пытался что-то вроде этого: <xsl:template match = "changeSet[sql[every $table in $tables satisfies contains(., $table)]]"> <xsl:copy> <xsl:copy-of select = "changeSet"/> </xsl:copy> </xsl:template>
Обратите внимание, что CATALOGATTRIBUTE
содержит CATALOG
, поэтому, если вы собираетесь использовать contains()
в качестве теста, отдельное тестирование CATALOGATTRIBUTE
излишне.
да, я знаю, что я изменил содержание на contains(., concat('${dbSchema}.',$table, ' '))
, но это все еще не сработало для меня.
Я думаю, что было бы лучше токенизировать содержимое sql
перед сравнением.
Рассмотрим следующий пример упрощенный:
XML
<databaseChangeLog>
<changeSet>
<sql>ALPHA,BRAVO,CHARLIE</sql>
</changeSet>
<changeSet>
<sql>BRAVO,CHARLIE,DELTA</sql>
</changeSet>
<changeSet>
<sql>DELTA,ECHO,FOXTROT</sql>
</changeSet>
<changeSet>
<sql>FOXTROT,GOLF,HOTEL</sql>
</changeSet>
</databaseChangeLog>
XSLT 2.0
<xsl:stylesheet version = "2.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
<xsl:output method = "xml" version = "1.0" encoding = "UTF-8" indent = "yes"/>
<xsl:param name = "tables" select = "('BRAVO', 'GOLF')"/>
<xsl:template match = "/databaseChangeLog">
<xsl:variable name = "group1" select = "changeSet[some $t in $tables satisfies contains(sql, $t)]" />
<xsl:copy>
<group1>
<xsl:copy-of select = "$group1"/>
</group1>
<group2>
<xsl:copy-of select = "changeSet except $group1"/>
</group2>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Результат
<?xml version = "1.0" encoding = "UTF-8"?>
<databaseChangeLog>
<group1>
<changeSet>
<sql>ALPHA,BRAVO,CHARLIE</sql>
</changeSet>
<changeSet>
<sql>BRAVO,CHARLIE,DELTA</sql>
</changeSet>
<changeSet>
<sql>FOXTROT,GOLF,HOTEL</sql>
</changeSet>
</group1>
<group2>
<changeSet>
<sql>DELTA,ECHO,FOXTROT</sql>
</changeSet>
</group2>
</databaseChangeLog>
Демо: https://xsltfiddle.liberty-development.net/jyRYYiP
Лучшим решением было бы использовать tokenize()
:
XSLT 2.0
<xsl:stylesheet version = "2.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
<xsl:output method = "xml" version = "1.0" encoding = "UTF-8" indent = "yes"/>
<xsl:param name = "tables" select = "('BRAVO', 'GOLF')"/>
<xsl:template match = "/databaseChangeLog">
<xsl:variable name = "group1" select = "changeSet[tokenize(sql, ',') = $tables]" />
<xsl:copy>
<group1>
<xsl:copy-of select = "$group1"/>
</group1>
<group2>
<xsl:copy-of select = "changeSet except $group1"/>
</group2>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Вы можете выбрать, например.
//changeSet[$sql[every $table in $tables satisfies contains($sql, $table)]]
, чтобы выбрать всеchangeSet
элементы, где строковое значение элементовsql
содержит каждую строку в переменнойtables
. Не уверен, что это поможет. Вы не объяснили, какой результат вы хотите.