Я новичок в xsltproc и хочу преобразовать этот XML:
<services>
<event name = "Something.zip">
<enabled>true</enabled>
<bindings>
<binding name = "Example">
<machine>example-name</machine>
<product>
<type>Shared</type>
<version>1.0</version>
<location>/path/to/something</location>
</product>
</binding>
</bindings>
</event>
</services>
На этот:
<services>
<event name = "Something.zip">
<enabled>true</enabled>
<bindings>
<binding name = "Example">
<machine>example-name</machine>
<product>
<type>Shared</type>
<version>1.0</version>
<location>/path/to/something</location>
</product>
</binding>
<binding name = "Example-1">
<machine>example-name-test</machine>
<product>
<type>Shared</type>
<version>1.0</version>
<location>/path/to/something</location>
</product>
</binding>
</bindings>
</event>
</services>
Вот пункты, которым я должен следовать:
Вставка.xsl, которую я написал:
<xsl:stylesheet version = "1.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" xmlns = "http://some/xml/namespace" xmlns:tc = "http://some/xml/namespace" exclude-result-prefixes = "tc">
<xsl:output method = "xml" indent = "yes"/>
<xsl:template match = "node()|@*">
<xsl:copy>
<xsl:apply-templates select = "node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match = "tc:binding">
<xsl:copy-of select = "."/>
<binding name = "{@name}-1">
<xsl:copy-of select = "*"/>
</binding>
</xsl:template>
</xsl:stylesheet>
С помощью y.arazim я продублировал привязку внутри привязок и изменил атрибут имени, как было предложено. Тем не менее, не могу решить, как изменить новый блок привязки примера 1.
P.S. Если у меня есть значение тега с пробелами, удалит ли xsltproc эти пробелы и оставит вместо этого закрытый тег? Если да, есть ли возможность избежать такого поведения?
Рабочая таблица стилей:
<xsl:stylesheet version = "1.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" xmlns = "http://some/xml/namespace" xmlns:tc = "http://some/xml/namespace" exclude-result-prefixes = "tc">
<xsl:output method = "xml" indent = "yes"/>
<xsl:template match = "node()|@*">
<xsl:copy>
<xsl:apply-templates select = "node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match = "tc:binding">
<xsl:copy-of select = "."/>
<binding name = "{@name}-1">
<machine>
<xsl:value-of select = "concat(tc:machine, '-test')"/>
</machine>
<xsl:copy-of select = "*[not(self::tc:machine)]"/>
</binding>
</xsl:template>
</xsl:stylesheet>
После долгих поисков окончательным решением (на основе ответа y.arazim) было получение элементов xml из указанного пространства имен путем добавления префикса перед элементом.





Почему-то ничего не происходит.
Это не правда. Он что-то делает: выдает ошибку:
Cannot add attributes to an element if children have been already added to the element.
Чтобы получить результат, который вы показываете, попробуйте что-то вроде:
<xsl:template match = "binding">
<xsl:copy-of select = "."/>
<binding name = "{@name}-1">
<machine>
<xsl:value-of select = "concat(machine, '-test')"/>
</machine>
<xsl:copy-of select = "product"/>
</binding>
</xsl:template>
Обратите внимание, что ваша таблица стилей объявляет пространство имен по умолчанию xmlns = "http://some/xml/namespace". Я не знаю, действительно ли вы этого хотите или это просто попытка «программирования вуду», но это существенно изменит результат.
Ваш вопрос о пространствах см.: https://www.w3.org/TR/1999/REC-xslt-19991116/#strip
Добавлено: вы можете увидеть, как это работает здесь.
Просто измените <xsl:copy-of select = "product"/> на <xsl:copy-of select = "*"/>
Вообще так и должно быть <xsl:copy-of select = "*[not(self::machine)]"/>. В противном случае вы получите еще одно появление элемента machine.
Я попробовал ваше решение и сделал копию существующего в данный момент блока привязки с именем Пример-1 (что хорошо), затем вставил в него еще один <machine>-test</machine> (что нехорошо) и, наконец, вставил это " привязка» в существующий в данный момент «блок привязки», а не в родительский элемент. Я обновил шаблон.
Это не тот результат, который я получаю с предоставленными вами данными (см. ссылку в моем ответе). Возможно, разница вызвана тем, что ваш ввод находится в пространстве имен и вы не можете правильно его настроить.
На самом деле, глядя на ваш отредактированный код, это совсем не то, что я предлагал.
Вы правы. Я проверил еще раз, и это работает, но я получаю два тега с именем «machine»: <machine>-test</machine> и <machine>example-name</machine>.
Опять же, я не вижу, чтобы на выходе я получал данные, которые вы предоставили. Вы смотрели демо-версию, которую я предоставил по ссылке?
Да, я видел скрипку. По какой-то причине рабочий пример работает отлично, но в моей ОС добавлен еще один тег <machine>-test</machine>. Это означает, что concat работает с пустым значением тега, а копия вообще не использует фильтр (хотя написано иначе).
Нет, это означает, что либо ваш ввод, либо ваш XSLT (или оба) различны. ИМХО, вы совершили серьезную ошибку, скрыв тот факт, что ваш входной XML находится в пространстве имен (в «целях безопасности» вы могли бы просто изменить URI пространства имен, как вы это сделали с XSLT), и теперь вы имеете дело с последствиями. К сожалению, я не знаю, как помочь вам с информацией, которую не вижу. И честно говоря, я уже потратил на это гораздо больше времени, чем планировал.
@Blurred_Vision Наиболее вероятная причина, по которой вы получаете значение -test, заключается в том, что вы вызываете concat(machine, '-test'), но поскольку machine находится в пространстве имен, из XML не возвращается никакое значение. Узнайте, как правильно обрабатывать пространства имен во входном XML: stackoverflow.com/a/34762628/3016153.
@michael.hor257k Великолепно. Большое спасибо, Михаил, упомянутая вами ссылка была для меня очень полезной и понятной.
@y.arazim Спасибо за ваш вклад и время, потраченное на эту проблему. Я отмечу ваш ответ как решение, потому что он был наиболее близким и внес наибольший вклад.
Я был бы склонен решить эту проблему, используя режимы:
<xsl:template match = "binding">
<xsl:copy-of select = "."/>
<xsl:apply-templates select = "." mode = "dup"/>
</xsl:template>
<xsl:template match = "binding/@name" mode = "dup">
<xsl:attribute name = "name">
<xsl:value-of select = "concat(., '-1')"/>
</xsl:attribute>
</xsl:template>
<xsl:template match = "binding/machine" mode = "dup">
<xsl:copy>
<xsl:value-of select = "concat(., '-test')"/>
</xsl:copy>
</xsl:template>
и убедитесь, что шаблон удостоверения также применим к режиму dup.
Как «убедиться, что шаблон удостоверения также применим к режиму dup» в XSLT 1.0?
Я не указал это подробно, потому что не использовал XSLT 1.0 более 20 лет, поэтому мне нужно поискать. Что вы могли бы сделать и сами. Похоже, вам нужно добавить еще одну копию правила шаблона удостоверения, которое определяет mode = "dup".
Я посмотрел это. Итак: 4 шаблона вместо одного? За что?
Для разделения задач, модульности и возможности повторного использования.
Это благородные принципы. Но когда вы настаиваете на их реализации, невзирая ни на какие другие соображения, для этого есть уродливое слово.
Ну, это оценочное суждение. Не зная предыстории проекта и его вероятных будущих направлений, невозможно предсказать, сколько усилий потребуется приложить для написания кода, который выдержит испытание временем.
Я очень этого хочу, но маскирую это в целях безопасности. Что, если содержимое блока привязки может измениться? Есть ли какая-либо альтернатива строке <xsl:copy-of select = "product"/>, чтобы она копировала все содержимое блока привязки независимо от того, какой тег находится внутри нее?