基于 csv 文件以数组作为值的 XSLT 构建映射
XSLT build map with array as a value based on csv file
我想基于csv文件构建地图。
地图声明:
<xsl:variable name="myMap" as="map(xs:string, array(xs:string))">
Csv 文件:
key1;value1
key1;value2
key2;value3
所以地图应该由两个元素组成:
key1 => 数组 ['value1', 'value2']
key2 => 数组 ['value3']
我试过像这样创建地图:
<xsl:variable name="myMap" as="map(xs:string, array(xs:string))">
<xsl:map>
<xsl:if test="unparsed-text-available($csv-file, $csv-encoding)">
<xsl:variable name="csv" select="unparsed-text($csv-file, $csv-encoding)"/>
<xsl:analyze-string select="$csv" regex="\r\n?|\n">
<xsl:non-matching-substring>
<xsl:variable name="row" select="tokenize(., '\t')"/>
<xsl:variable name="key" select="$row[1]"/>
<xsl:variable name="array_element" select="$row[2]"/>
<xsl:map-entry key="$key" select="$array_element"/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:if>
</xsl:map>
</xsl:variable>
但我找不到合并地图条目的方法。
我的第二种方法是先声明地图:
<xsl:variable name="myMap" as="map(xs:string, array(xs:string))">
<xsl:map/>
</xsl:variable>
然后我尝试根据 csv 文件内容填充它,如下所示:
<xsl:if test="unparsed-text-available($csv-file, $csv-encoding)">
<xsl:variable name="csv" select="unparsed-text($csv-file, $csv-encoding)" />
<xsl:analyze-string select="$csv" regex="\r\n?|\n">
<xsl:non-matching-substring>
<xsl:variable name="row" select="tokenize(., '\t')"/>
<xsl:variable name="key" as="xs:string" select="$row[1]"/>
<xsl:variable name="value" as="xs:string" select="$row[2]"/>
<xsl:choose>
<xsl:when test="map:contains($myMap, $key)">
<xsl:variable name="valueArray" select="map:get($myMap,$key)"/>
<xsl:sequence select="array:append($valueArray, $value)" />
<xsl:sequence select="map:put($myMap, $key, $valueArray) />
</xsl:when>
<xsl:otherwise>
<xsl:variable name="valueArray" as="array(xs:string)" select="[]"/>
<xsl:sequence select="array:append($valueArray, $value)" />
<xsl:sequence select="map:put($myMap, $key, $valueArray) />
</xsl:otherwise>
</xsl:choose>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:if>
是否可以从模板调用 array:append 和 map:put 方法?
我认为,鉴于您使用 XSLT 3,您可以将其视为分组问题,您可以将 CSV 中的行分组到 substring-before(., ';')
:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:param name="csv-string" as="xs:string">key1;value1
key1;value2
key2;value3</xsl:param>
<xsl:variable name="myMap" as="map(xs:string, array(xs:string))">
<xsl:map>
<xsl:for-each-group select="tokenize($csv-string, '\r?\n')[normalize-space()]" group-by="substring-before(., ';')">
<xsl:map-entry key="current-grouping-key()" select="array{ current-group()!tokenize(substring-after(., ';'), ';') }"/>
</xsl:for-each-group>
</xsl:map>
</xsl:variable>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="json" indent="yes" />
<xsl:template match="/" name="xsl:initial-template">
<xsl:sequence select="$myMap"/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/nc4NzRb
对于示例,我已经内联了您的 CSV 中的内容,但您也可以使用 <xsl:for-each-group select="unparsed-text-lines('file.csv')" group-by="substring-before(., ';')">
而不是从文件加载。
至于构建和合并地图,如果您不需要 array(xs:string)
作为地图值但可以接受一系列字符串,您可以使用 map:merge
函数和一个选项合并同一键的值:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
exclude-result-prefixes="#all"
version="3.0">
<xsl:param name="csv-string" as="xs:string">key1;value1
key1;value2
key2;value3</xsl:param>
<xsl:variable name="myMap" as="map(xs:string, xs:string*)"
select="map:merge(
tokenize($csv-string, '\r?\n')[normalize-space()]
!
(
let $values := tokenize(., ';')
return map { head($values) : tail($values) }
),
map { 'duplicates' : 'combine' }
)"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="adaptive" indent="yes" />
<xsl:template match="/" name="xsl:initial-template">
<xsl:sequence select="$myMap"/>
</xsl:template>
</xsl:stylesheet>
我想基于csv文件构建地图。
地图声明:
<xsl:variable name="myMap" as="map(xs:string, array(xs:string))">
Csv 文件:
key1;value1
key1;value2
key2;value3
所以地图应该由两个元素组成: key1 => 数组 ['value1', 'value2'] key2 => 数组 ['value3']
我试过像这样创建地图:
<xsl:variable name="myMap" as="map(xs:string, array(xs:string))">
<xsl:map>
<xsl:if test="unparsed-text-available($csv-file, $csv-encoding)">
<xsl:variable name="csv" select="unparsed-text($csv-file, $csv-encoding)"/>
<xsl:analyze-string select="$csv" regex="\r\n?|\n">
<xsl:non-matching-substring>
<xsl:variable name="row" select="tokenize(., '\t')"/>
<xsl:variable name="key" select="$row[1]"/>
<xsl:variable name="array_element" select="$row[2]"/>
<xsl:map-entry key="$key" select="$array_element"/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:if>
</xsl:map>
</xsl:variable>
但我找不到合并地图条目的方法。
我的第二种方法是先声明地图:
<xsl:variable name="myMap" as="map(xs:string, array(xs:string))">
<xsl:map/>
</xsl:variable>
然后我尝试根据 csv 文件内容填充它,如下所示:
<xsl:if test="unparsed-text-available($csv-file, $csv-encoding)">
<xsl:variable name="csv" select="unparsed-text($csv-file, $csv-encoding)" />
<xsl:analyze-string select="$csv" regex="\r\n?|\n">
<xsl:non-matching-substring>
<xsl:variable name="row" select="tokenize(., '\t')"/>
<xsl:variable name="key" as="xs:string" select="$row[1]"/>
<xsl:variable name="value" as="xs:string" select="$row[2]"/>
<xsl:choose>
<xsl:when test="map:contains($myMap, $key)">
<xsl:variable name="valueArray" select="map:get($myMap,$key)"/>
<xsl:sequence select="array:append($valueArray, $value)" />
<xsl:sequence select="map:put($myMap, $key, $valueArray) />
</xsl:when>
<xsl:otherwise>
<xsl:variable name="valueArray" as="array(xs:string)" select="[]"/>
<xsl:sequence select="array:append($valueArray, $value)" />
<xsl:sequence select="map:put($myMap, $key, $valueArray) />
</xsl:otherwise>
</xsl:choose>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:if>
是否可以从模板调用 array:append 和 map:put 方法?
我认为,鉴于您使用 XSLT 3,您可以将其视为分组问题,您可以将 CSV 中的行分组到 substring-before(., ';')
:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:param name="csv-string" as="xs:string">key1;value1
key1;value2
key2;value3</xsl:param>
<xsl:variable name="myMap" as="map(xs:string, array(xs:string))">
<xsl:map>
<xsl:for-each-group select="tokenize($csv-string, '\r?\n')[normalize-space()]" group-by="substring-before(., ';')">
<xsl:map-entry key="current-grouping-key()" select="array{ current-group()!tokenize(substring-after(., ';'), ';') }"/>
</xsl:for-each-group>
</xsl:map>
</xsl:variable>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="json" indent="yes" />
<xsl:template match="/" name="xsl:initial-template">
<xsl:sequence select="$myMap"/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/nc4NzRb
对于示例,我已经内联了您的 CSV 中的内容,但您也可以使用 <xsl:for-each-group select="unparsed-text-lines('file.csv')" group-by="substring-before(., ';')">
而不是从文件加载。
至于构建和合并地图,如果您不需要 array(xs:string)
作为地图值但可以接受一系列字符串,您可以使用 map:merge
函数和一个选项合并同一键的值:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
exclude-result-prefixes="#all"
version="3.0">
<xsl:param name="csv-string" as="xs:string">key1;value1
key1;value2
key2;value3</xsl:param>
<xsl:variable name="myMap" as="map(xs:string, xs:string*)"
select="map:merge(
tokenize($csv-string, '\r?\n')[normalize-space()]
!
(
let $values := tokenize(., ';')
return map { head($values) : tail($values) }
),
map { 'duplicates' : 'combine' }
)"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="adaptive" indent="yes" />
<xsl:template match="/" name="xsl:initial-template">
<xsl:sequence select="$myMap"/>
</xsl:template>
</xsl:stylesheet>