У меня есть плоский файл, который я хотел бы преобразовать в XML с помощью XSLT. Первый символ каждой строки представляет собой блок информации, и я хотел бы сгруппировать все вместе. Строки могут начинаться с нескольких символов. Что я хочу сделать, так это сгруппировать блоки строк, которые находятся между символом 1
.
Вот как выглядит входной файл:
0xxxxxxxxxxxxxxxxxxxxxxxxx
1xxxxxxxxxxxxxxxxxxxxxxxxx
2xxxxxxxxxxxxxxxxxxxxxxxxx
3xxxxxxxxxxxxxxxxxxxxxxxxx
5xxxxxxxxxxxxxxxxxxxxxxxxx
8xxxxxxxxxxxxxxxxxxxxxxxxx
1xxxxxxxxxxxxxxxxxxxxxxxxx
2xxxxxxxxxxxxxxxxxxxxxxxxx
5xxxxxxxxxxxxxxxxxxxxxxxxx
8xxxxxxxxxxxxxxxxxxxxxxxxx
1xxxxxxxxxxxxxxxxxxxxxxxxx
8xxxxxxxxxxxxxxxxxxxxxxxxx
9xxxxxxxxxxxxxxxxxxxxxxxxx
x
просто представляет данные в строке, о которых я могу позаботиться. Что я хочу сделать, так это продукт:
<Root>
<Header> // O line
</Header>
<Summary id="xxxxx"> // First 1 line
<data_from_2>
</data_from_2>
<data_from_3>
</data_from_3>
<data_from_5>
</data_from_5>
<data_from_8>
</data_from_8>
</Summary>
<Summary id="xxxxx"> // Second 1 line
<data_from_2>
</data_from_2>
<data_from_3>
</data_from_3>
<data_from_5>
</data_from_5>
<data_from_8>
</data_from_8>
</Summary>
<Summary id="xxxxx"> // Third 1 line
<data_from_2>
</data_from_2>
<data_from_3>
</data_from_3>
<data_from_5>
</data_from_5>
<data_from_8>
</data_from_8>
</Summary>
<Footer> // 9 line
</Footer>
</Root>
Сложность в том, что неизвестно, сколько строк будет под линией 1
. Может быть только одна строка для группировки под ней или много других строк.
Вот мой начальный XSLT (в настоящее время он создает плоскую структуру):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="newline" select="'
'" />
<xsl:variable name="tab" select="'	'" />
<xsl:template match="/">
<xsl:value-of select="$newline"/>
<FirstData>
<xsl:value-of select="$newline"/>
<xsl:for-each select="tokenize(.,'\r?\n')">
<!-- DETERMINE WHAT FIRST CHAR LOOKS LIKE -->
<xsl:variable name="lineToken" select="substring(., 1, 1)"/>
<!-- HEADER -->
<xsl:if test="$lineToken='0'">
<xsl:variable name="periodStart" select="substring(., 2, 6)"/>
<xsl:value-of select="$tab"/><HEADER><xsl:value-of select="$newline"/>
<xsl:value-of select="$tab"/><xsl:value-of select="$tab"/><Period_start_date><xsl:sequence select="$periodStart"/></Period_start_date><xsl:value-of select="$newline"/>
<xsl:value-of select="$tab"/></HEADER><xsl:value-of select="$newline"/>
</xsl:if>
<!-- SUMMARY -->
<xsl:if test="$lineToken='1'">
<xsl:value-of select="$tab"/><xsl:element name="SUMMARY">
<xsl:attribute name="ID"><xsl:value-of select ="substring(., 2, 11)"/></xsl:attribute>
<xsl:value-of select="$newline"/>
<xsl:variable name="ID" select="substring(., 2, 11)"/>
<xsl:variable name="batchDate" select="substring(., 13, 4)"/>
<xsl:value-of select="$tab"/><xsl:value-of select="$tab"/><ID><xsl:sequence select="$fdmsMerchantNum"/></FDMS_Merchant_Number><xsl:value-of select="$newline"/>
<xsl:value-of select="$tab"/><xsl:value-of select="$tab"/><Batch_Date><xsl:sequence select="$batchDate"/></Batch_Date><xsl:value-of select="$newline"/>
<xsl:value-of select="$tab"/></xsl:element><xsl:value-of select="$newline"/>
</xsl:if>
<!-- Data 2 -->
<xsl:if test="$lineToken='2'">
<xsl:value-of select="$tab"/><Data_2><xsl:value-of select="$newline"/>
<xsl:variable name="Sales" select="substring(., 2, 3)"/>
<xsl:value-of select="$tab"/><xsl:value-of select="$tab"/><Sales><xsl:sequence select="$Sales"/></Sales><xsl:value-of select="$newline"/>
<xsl:value-of select="$tab"/></Data_2><xsl:value-of select="$newline"/>
</xsl:if>
<!-- Data 3 -->
<xsl:if test="$lineToken='3'">
<xsl:value-of select="$tab"/><Data_3><xsl:value-of select="$newline"/>
<xsl:variable name="Sales" select="substring(., 2, 3)"/>
<xsl:value-of select="$tab"/><xsl:value-of select="$tab"/><Sales><xsl:sequence select="$Sales"/></Sales><xsl:value-of select="$newline"/>
<xsl:value-of select="$tab"/></Data_3><xsl:value-of select="$newline"/>
</xsl:if>
<!-- Data 5 and Data 8 elements are identical -->
</xsl:for-each>
</Root>
</xsl:template>
</xsl:stylesheet>
Что я хочу сделать, так это иметь возможность вложить элементы данных 2 и данных 3 в сводный элемент, но как мне обработать эти строки, а затем запустить новый сводный элемент для следующей встреченной 1 строки?
Прошу прощения за все общие вещи, у меня много данных, с которыми нужно работать, и я пытаюсь упростить вопрос. Если потребуется дополнительная информация, дайте мне знать.
1 ответ
Это похоже на задание для for-each-group group-starting-with
, но в XSLT 2.0 вы можете использовать это только с последовательностями узлов, а не с последовательностями строк. Поэтому я бы сначала обернул строки, которые вы получаете от tokenize(.,'\r?\n')
, в элемент, например.
<xsl:variable name="lines" as="element(line)*">
<xsl:for-each select="tokenize(.,'\r?\n')">
<line><xsl:value-of select="."/></line>
</xsl:for-each>
</xsl:variable>
Тогда я бы использовал
<xsl:for-each-group select="$lines" group-starting-with="line[starts-with(., '1')]">
<xsl:choose>
<xsl:when test="not(self::line[starts-with(., '1')])">
<!-- header -->
<Header><xsl:value-of select="substring(., 2)"/></Header>
</xsl:when>
<xsl:otherwise>
<Summary id="{substring(., 2)}">
<!-- now use for-each select="if (position() eq last()) then current-group()[position() gt 1 and position() ne last()] else current-group()[position() gt 1]" or apply-templates to output the lines-->
<xsl:for-each select="if (position() eq last()) then current-group()[position() gt 1 and position() ne last()] else current-group()[position() gt 1]">
<xsl:element name="data_from_{substring(., 1, 1)}"><xsl:value-of select="substring(., 2)"/></xsl:element>
</xsl:for-each>
</Summary>
<xsl:if test="position() eq last()">
<Footer>
<xsl:value-of select="substring(current-group()[last()], 2)"/>
</Footer>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
В группу.
Я нашел время написать рабочий образец, XSLT - это
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:param name="text-url" select="'test2014032901.txt'"/>
<xsl:output indent="yes"/>
<xsl:template name="main">
<xsl:variable name="text" select="unparsed-text($text-url)"/>
<xsl:variable name="lines" as="element(line)*">
<xsl:for-each select="tokenize($text,'\r?\n')[normalize-space()]">
<line><xsl:value-of select="."/></line>
</xsl:for-each>
</xsl:variable>
<Root>
<xsl:for-each-group select="$lines" group-starting-with="line[starts-with(., '1')]">
<xsl:choose>
<xsl:when test="not(self::line[starts-with(., '1')])">
<!-- header -->
<xsl:variable name="periodStart" select="substring(., 2, 6)"/>
<Header>
<Period_start_date>
<xsl:value-of select="$periodStart"/>
</Period_start_date>
</Header>
</xsl:when>
<xsl:otherwise>
<Summary id="{substring(., 2, 11)}">
<ID><xsl:value-of select="substring(., 2, 11)"/></ID>
<Batch_Date><xsl:value-of select="substring(., 13, 4)"/></Batch_Date>
<!-- now use for-each select="if (position() eq last()) then current-group()[position() gt 1 and position() ne last()] else current-group()[position() gt 1]" or apply-templates to output the lines-->
<xsl:for-each select="if (position() eq last()) then current-group()[position() gt 1 and position() ne last()] else current-group()[position() gt 1]">
<xsl:element name="data_from_{substring(., 1, 1)}">
<Sales>
<xsl:value-of select="substring(., 2, 3)"/>
</Sales>
</xsl:element>
</xsl:for-each>
</Summary>
<xsl:if test="position() eq last()">
<Footer>
<xsl:value-of select="substring(current-group()[last()], 2)"/>
</Footer>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</Root>
</xsl:template>
</xsl:stylesheet>
Имя текстового файла передается в качестве параметра text-url
, а таблица стилей должна начинаться с it:main
(шаблон с именем main
для Saxon)), затем я получаю результат
<Root>
<Header>
<Period_start_date>xxxxxx</Period_start_date>
</Header>
<Summary id="xxxxxxxxxxx">
<ID>xxxxxxxxxxx</ID>
<Batch_Date>xxxx</Batch_Date>
<data_from_2>
<Sales>xxx</Sales>
</data_from_2>
<data_from_3>
<Sales>xxx</Sales>
</data_from_3>
<data_from_5>
<Sales>xxx</Sales>
</data_from_5>
<data_from_8>
<Sales>xxx</Sales>
</data_from_8>
</Summary>
<Summary id="xxxxxxxxxxx">
<ID>xxxxxxxxxxx</ID>
<Batch_Date>xxxx</Batch_Date>
<data_from_2>
<Sales>xxx</Sales>
</data_from_2>
<data_from_5>
<Sales>xxx</Sales>
</data_from_5>
<data_from_8>
<Sales>xxx</Sales>
</data_from_8>
</Summary>
<Summary id="xxxxxxxxxxx">
<ID>xxxxxxxxxxx</ID>
<Batch_Date>xxxx</Batch_Date>
<data_from_8>
<Sales>xxx</Sales>
</data_from_8>
</Summary>
<Footer>xxxxxxxxxxxxxxxxxxxxxxxxx</Footer>
</Root>
Похожие вопросы
Новые вопросы
xml
XML (Extensible Markup Language) - это формат структурированного документа, определяющий правила кодирования текста. При использовании этого тега включайте дополнительные теги, такие как язык программирования, наборы инструментов, используемые технологии XML и другие теги, описывающие среду, в которой опубликована проблема. Гибкость XML обеспечивает широкий спектр применений для передачи человеческих и машинных данных, в том числе конкретных инструментов и библиотек.
A sequence of more than one item is not allowed as the value of variable $lines (<line/>, <line/>, ...)
. Я все еще новичок в XSL, поэтому прошу прощения, если не полностью понимаю код.*
, я это исправил.