Что не так с этим подходом слияния и дедупликации?
Учитывая этот исходный документ:
<?xml version="1.0" encoding="utf-8"?>
<config>
<group name="global">
<globals>
<item grp="db" prop="userid" value="foo"/>
<item grp="db" prop="passwd" value="bar"/>
<item grp="log" prop="level" value="debug"/>
<item grp="log" prop="filename" value="red.log"/>
</globals>
</group>
<group name="dev">
<globals>
<item grp="db" prop="server" value="dev_sql_1"/>
</globals>
<locals>
<item grp="db" prop="catalog" value="red_db_local"/>
<item grp="db" prop="passwd" value="dev_passwd"/>
<item grp="log" prop="level" value="info"/>
</locals>
</group>
<group name="qa">
<globals>
<item grp="db" prop="server" value="qa_sql_1"/>
<item grp="db" prop="catalog" value="qa_db"/> <!-- this is wonky, but may happen -->
</globals>
<locals>
<item grp="db" prop="catalog" value="red_db_local"/> <!-- this should beat 'qa_db' from ../globals/item[@grp='db' and prop='catalog'] -->
<item grp="db" prop="passwd" value="qa_passwd"/>
<item grp="log" prop="level" value="critical"/>
</locals>
</group>
<group name="prod">
<globals>
<item grp="db" prop="server" value="prod_sql_1"/>
</globals>
<locals>
<item grp="db" prop="catalog" value="prod_db_local"/>
<item grp="db" prop="passwd" value="prod_passwd"/>
<item grp="log" prop="level" value="critical"/>
</locals>
</group>
</config>
и параметр, который является одной из доступных сред, я хотел бы закончить с объединенным и дедуплицированным набором узлов, сохраняя самые определенные значения. Итак, для "прод":
<config>
<item grp="db" prop="userid" value="foo"/>
<item grp="log" prop="filename" value="red.log"/>
<item grp="db" prop="server" value="prod_sql_1"/>
<item grp="db" prop="catalog" value="prod_db_local"/>
<item grp="db" prop="passwd" value="prod_passwd"/>
<item grp="log" prop="level" value="critical"/>
</config>
Я очень новичок в использовании ключей в XSLT 1.0, и я придумал эту таблицу стилей, которая работает для 'prod', но не для 'dev' или 'qa':
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:param name="environment"/>
<!--
using | to create a union of top-level global items and and env-specific items
-->
<xsl:variable name="all-items"
select="/config/group[@name='global']/globals/item |
//group[@name=$environment]//item"/>
<xsl:key name="dupes" match="item" use="concat(@grp,'|',@prop)"/>
<xsl:template match="/config">
<xsl:copy>
<xsl:copy-of
select="$all-items[generate-id() = generate-id(key('dupes',
concat(@grp,'|',@prop))[last()])]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Это подход, к которому я стремлюсь:
- объединить все соответствующие
<item.../>
узлы в наборе узлов с объединением|
- сгруппировать этот набор узлов по
@grp
а также@prop
атрибуты - сохранить последний узел в любой из результирующих групп (de-dupe)
Так как я новичок в ключах, я могу только сказать, что я думаю, что это немного кода,
<xsl:copy-of select="$all-items[generate-id() = generate-id(key('dupes',
concat(@grp,'|',@prop))[last()])]"/>
выбирает last()
узел из набора узлов повторяющихся элементов, но при запуске с 'dev' или 'qa' я получаю следующее:
REG zacharyyoung$ xsltproc --stringparam environment dev config3.xsl config3.xml
<config>
<item grp="db" prop="userid" value="foo"/>
<item grp="log" prop="filename" value="red.log"/>
</config>
REG zacharyyoung$ xsltproc --stringparam environment qa config3.xsl config3.xml
<config>
<item grp="db" prop="userid" value="foo"/>
<item grp="log" prop="filename" value="red.log"/>
</config>
Я проверил промежуточную переменную all-items
для каждого параметра среды, и кажется, что, по крайней мере, так много работает правильно.
Если я перееду <group name="qa"/>
на дно, как:
<config>
<group name="global">...</group>
<group name="dev">...</group>
<group name="prod">...</group>
<group name="qa">...</group>
<config>
тогда запуск его с 'qa' работает:
REG zacharyyoung$ xsltproc --stringparam environment qa config3.xsl config3.xml
<config>
<item grp="db" prop="userid" value="foo"/>
<item grp="log" prop="filename" value="red.log"/>
<item grp="db" prop="server" value="qa_sql_1"/>
<item grp="db" prop="catalog" value="red_db_local"/>
<item grp="db" prop="passwd" value="qa_passwd"/>
<item grp="log" prop="level" value="critical"/>
</config>
Итак, почему позиция <group name="...">...</group>
Я выбираю материю? В частности, почему он работает только в последней позиции, и как мне заставить его работать для любой позиции?
РЕДАКТИРОВАТЬ 1
Когда я изолирую данные от $all-items
(для любой среды) и поместите его в свой собственный файл, XSL работает правильно. Следующий пример - объединение глобалов и 'dev':
<config>
<item grp="db" prop="userid" value="foo"/>
<item grp="db" prop="passwd" value="bar"/>
<item grp="log" prop="level" value="debug"/>
<item grp="log" prop="filename" value="red.log"/>
<item grp="db" prop="server" value="dev_sql_1"/>
<item grp="db" prop="catalog" value="red_db_local"/>
<item grp="db" prop="passwd" value="dev_passwd"/>
<item grp="log" prop="level" value="info"/>
</config>
и этот XSL:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="dupes" match="item" use="concat(@grp,'|',@prop)"/>
<xsl:template match="/config">
<xsl:copy>
<xsl:copy-of
select="item[generate-id() = generate-id(key('dupes',
concat(@grp,'|',@prop))[last()])]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
результаты в:
REG zacharyyoung$ xsltproc config4.xsl config4.xml
<config>
<item grp="db" prop="userid" value="foo"/>
<item grp="log" prop="filename" value="red.log"/>
<item grp="db" prop="server" value="dev_sql_1"/>
<item grp="db" prop="catalog" value="red_db_local"/>
<item grp="db" prop="passwd" value="dev_passwd"/>
<item grp="log" prop="level" value="info"/>
</config>
Итак, теперь, кажется, до переменной all-items
?
Спасибо.
2 ответа
Я не уверен, почему группировка не работает (я постараюсь взглянуть на нее в ближайшее время), но вы также можете достичь желаемого результата без использования ключей вообще.
Эта таблица стилей 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:param name="environment" select="'qa'"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="group">
<xsl:if test="@name = $environment">
<xsl:apply-templates select="/config/group[@name='global']/globals/item[not(@prop = /config/group[@name='prod']/locals/item/@prop)]"/>
<xsl:apply-templates/>
</xsl:if>
</xsl:template>
<xsl:template match="globals|locals">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
Применительно к вашему входному XML выдает желаемый результат:
<config>
<item grp="db" prop="userid" value="foo"/>
<item grp="log" prop="filename" value="red.log"/>
<item grp="db" prop="server" value="qa_sql_1"/>
<item grp="db" prop="catalog" value="red_db_local"/>
<item grp="db" prop="passwd" value="qa_passwd"/>
<item grp="log" prop="level" value="critical"/>
</config>
Это также работает для "prod" и "dev".
Редактировать: Удалена переменная из предиката.
Исходя из ответа DevNull (который отвечает на заданный вопрос), я внес следующие изменения (для некоторых неустановленных требований):
- Добавлены глобальные переменные окружения для смешивания, если там введено повторяющееся значение
- Удалено преобразование идентичности, так как оно не соответствует моим потребностям.
Вот полная таблица стилей:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="environment" select="'qa'"/>
<xsl:template match="/config">
<xsl:copy>
<xsl:copy-of select="/config/group[@name='global']/globals/item[not(@prop = /config/group[@name=$environment]//item/@prop)]"/>
<xsl:copy-of select="/config/group[@name=$environment]/globals/item[not(@prop = /config/group[@name=$environment]/locals/item/@prop)]"/>
<xsl:copy-of select="/config/group[@name=$environment]/locals/item"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Я все еще надеюсь выяснить, что не так с ключами в моем первоначальном вопросе.