Это полное XSLT-преобразование :
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vUpper" select=
"'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:variable name="vLower" select=
"'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="vDigits" select=
"'0123456789'"/>
<xsl:variable name="vAlphaNum" select=
"concat($vUpper, $vLower, $vDigits, '_')"/>
<xsl:template match="detail">
<xsl:element name=
"{translate(@type,
translate(@type, $vAlphaNum, ''),
'______________________________'
)}">
<xsl:apply-templates select=
"@*[not(name()='type')]|*"/>
</xsl:element>
</xsl:template>
<xsl:template match="detail/@*">
<xsl:element name=
"{translate(name(),
translate(name(), $vAlphaNum, ''),
'______________________________'
)}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<xsl:template match="property[@name='reference']">
<reference>
<xsl:value-of select="."/>
</reference>
</xsl:template>
<xsl:template match="property[@name='Month YYYY']">
<date>
<xsl:value-of select="."/>
</date>
</xsl:template>
<xsl:template match="property[@name='City, State']">
<location>
<xsl:value-of select="."/>
</location>
</xsl:template>
</xsl:stylesheet>
при применении к предоставленному документу XML :
<detail type="Courses Taken" gm_recid="FNBYHVW(,7()E)S" >
<properties>
<property name="reference" >
<property_string>M2</property_string>
</property>
<property name="Month YYYY" db_name="TITLE" >
<property_string></property_string>
</property>
<property name="City, State" db_name="LINKACCT" >
<property_string></property_string>
</property>
</properties>
</detail>
дает желаемый, правильный результат :
<Courses_Taken>
<gm_recid>FNBYHVW(,7()E)S</gm_recid>
<reference>M2</reference>
<date/>
<location/>
</Courses_Taken>
Объяснение : Использование варианта метода двойного перевода *1019*, впервые предложенного @Michael Kay:
Если у нас есть строка $s
и строка только допустимых символов $valid
, и мы хотим получить от $s
другую строку $s2
, которая содержит только действительный символ $s
(в их первоначальном порядке ), это может быть достигнуто путем вычисления следующего выражения XPath:
translate($s, translate($s, $valid, ''), '')
В данном конкретном случае мы хотим не удалять недопустимые символы, а заменить каждый из них на "_"
.
Мы предполагаем, что имя кандидата не будет превышать длину 30 , и используем строку замены фиксированной длины (это, конечно, может быть сделано так, как хотелось бы):
translate($s,
translate($s, $valid, ''),
'______________________________')