Вот что я бы сделал:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<!-- identity template to copy everything unless otherwise noted -->
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<!-- match the first text node directly following a <c> element -->
<xsl:template match="text()[preceding-sibling::node()[1][self::c]]">
<!-- ...and change its contents -->
<xsl:text>Hello world</xsl:text>
</xsl:template>
</xsl:stylesheet>
Обратите внимание, что текстовые узлы содержат «окружающие» пробелы - в примере XML в вопросе сопоставленный текстовый узел является только пробелом, поэтому вышеописанное работает. Он перестанет работать, как только входной документ будет выглядеть так:
<a><b/><c/></a>
потому что здесь нет текстового узла, следующего за <c>
. Так что, если это слишком хрупко для вашего варианта использования, альтернативой будет:
<!-- <c> nodes get a new adjacent text node -->
<xsl:template match="c">
<xsl:copy-of select="." />
<xsl:text>Hello world</xsl:text>
</xsl:template>
<!-- make sure to remove the first text node directly following a <c> node-->
<xsl:template match="text()[preceding-sibling::node()[1][self::c]]" />
В любом случае такие вещи, как приведенные выше, проясняют, почему лучше не смешивать текстовые узлы и узлы элементов. Это не всегда возможно (см. XHTML). Но когда у вас есть такая возможность, а XML должен быть просто контейнером для структурных данных, отказ от смешанного контента облегчает вашу жизнь.