Как я уже говорил в комментариях, это огромная работа, и качество результата будет зависеть от того, насколько последовательны ваши данные.
Предполагая, что:
- первое слово (и только первое слово) - это номер улицы;
- второе слово может быть идентификатором направления улицы из известного списка идентификаторов направления улицы;
- за названием улицы следует идентификатор типа улицы, из известного списка идентификаторов типа улицы
вы можете использовать следующую таблицу стилей в качестве отправной точки:
XSLT 1.0 (+ набор узлов EXSLT)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Field[@Name='address']">
<!-- tokenize to words -->
<xsl:variable name="tokens">
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="words" select="exsl:node-set($tokens)/token" />
<!-- street direction exists? -->
<xsl:variable name="street-direction-exists" select="$words[2]='N' or $words[2]='E' or $words[2]='S' or $words[2]='W' or $words[2]='NE' or $words[2]='NW' or $words[2]='SE' or $words[2]='SW'" />
<!-- find position of street type -->
<xsl:variable name="street-type-index">
<xsl:call-template name="get-street-type-index">
<xsl:with-param name="words" select="$words"/>
</xsl:call-template>
</xsl:variable>
<!-- output -->
<Address>
<HouseNumber>
<xsl:value-of select="$words[1]"/>
</HouseNumber>
<xsl:if test="$street-direction-exists">
<StreetDirection>
<xsl:value-of select="$words[2]"/>
</StreetDirection>
</xsl:if>
<StreetName>
<xsl:for-each select="$words[1 + $street-direction-exists < position() and position() < $street-type-index]">
<xsl:value-of select="."/>
<xsl:if test="position() != last()">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:for-each>
</StreetName>
<StreetType>
<xsl:value-of select="$words[number($street-type-index)]"/>
</StreetType>
<xsl:if test="count($words) > $street-type-index">
<Unit>
<xsl:for-each select="$words[position() > $street-type-index]">
<xsl:value-of select="."/>
<xsl:if test="position() != last()">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:for-each>
</Unit>
</xsl:if>
</Address>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="text"/>
<xsl:param name="delimiter" select="' '"/>
<token>
<xsl:value-of select="substring-before(concat($text, $delimiter), $delimiter)" />
</token>
<xsl:if test="contains($text, $delimiter)">
<!-- recursive call -->
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="substring-after($text, $delimiter)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="get-street-type-index">
<xsl:param name="words"/>
<xsl:variable name="street-types">
<street-type>AVE</street-type>
<street-type>STR</street-type>
<!-- add more types here (or use an external XML document for them) -->
</xsl:variable>
<xsl:choose>
<xsl:when test="$words[last()] = exsl:node-set($street-types)/street-type">
<xsl:value-of select="count($words)"/>
</xsl:when>
<xsl:when test="$words">
<!-- recursive call -->
<xsl:call-template name="get-street-type-index">
<xsl:with-param name="words" select="$words[position() != last()]"/>
</xsl:call-template>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Применяется к следующему тестовому входу:
XML
<Fields>
<Field Name="address">123 Old Oak Tree AVE</Field>
<Field Name="address">45B NE Broadway STR</Field>
<Field Name="address">6789 Maple Syrup AVE Room 800</Field>
<Field Name="address">1024 W Three Elm Trees AVE APT 321</Field>
<Field Name="address">1024 False AVE Maria STR Unit 8071</Field>
</Fields>
результат будет:
Результат
<?xml version="1.0" encoding="utf-16"?>
<Fields>
<Address>
<HouseNumber>123</HouseNumber>
<StreetName>Old Oak Tree</StreetName>
<StreetType>AVE</StreetType>
</Address>
<Address>
<HouseNumber>45B</HouseNumber>
<StreetDirection>NE</StreetDirection>
<StreetName>Broadway</StreetName>
<StreetType>STR</StreetType>
</Address>
<Address>
<HouseNumber>6789</HouseNumber>
<StreetName>Maple Syrup</StreetName>
<StreetType>AVE</StreetType>
<Unit>Room 800</Unit>
</Address>
<Address>
<HouseNumber>1024</HouseNumber>
<StreetDirection>W</StreetDirection>
<StreetName>Three Elm Trees</StreetName>
<StreetType>AVE</StreetType>
<Unit>APT 321</Unit>
</Address>
<Address>
<HouseNumber>1024</HouseNumber>
<StreetName>False AVE Maria</StreetName>
<StreetType>STR</StreetType>
<Unit>Unit 8071</Unit>
</Address>
</Fields>
Примечание : вам может понадобиться использовать собственное расширение Microsoft node-set()
функция вместо EXSLT.