Карта построения XSLT с массивом в качестве значения на основе файла CSV - PullRequest
0 голосов
/ 22 октября 2018

Я хотел бы построить карту на основе файла CSV.

Объявление карты:

<xsl:variable name="myMap" as="map(xs:string, array(xs:string))">

Файл CSV:

key1;value1
key1;value2
key2;value3

Таким образом, карта должна состоять издва элемента: key1 => array ['value1', 'value2'] key2 => array ['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 из шаблона?

1 Ответ

0 голосов
/ 22 октября 2018

Я думаю, что, учитывая, что вы используете 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

...