XSLT构建映射,将基于数组的数组作为基于csv文件的值

时间:2018-10-22 16:23:11

标签: dictionary xslt saxon

我想基于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>

但是我找不到合并地图条目的方法。

我的第二种方法是首先声明map:

<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方法?

1 个答案:

答案 0 :(得分:0)

我认为,假设您使用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>

https://xsltfiddle.liberty-development.net/nc4NzRb/1