Пакетная обработка файлов с разделителями табуляции в XSLT
У меня есть XML-файл со списком из 92 текстовых файлов с разделителями табуляции:
<?xml version="1.0" encoding="UTF-8"?>
<dumpSet>
<dump filename="file_one.txt"/>
<dump filename="file_two.txt"/>
<dump filename="file_three.txt"/>
...
</dumpSet>
Первая строка в каждом файле содержит имена полей для последующих строк. Это всего лишь пример. Имена и количество элементов будут варьироваться в зависимости от записи. У большинства будет около 50 имен полей.
Title Translated Title Watch Video Interviewee Interviewer
Interview with Barack Obama Obama, Barack Walters, Barbara
Interview with Sarah Palin Palin, Sarah Couric, Katie Smith, John
...
Oxygen XML Editor имеет функцию импорта, которая может конвертировать текстовые файлы в XML, но, насколько я знаю, это невозможно сделать в пакетном процессе с несколькими файлами. До сих пор часть пакетной обработки не была проблемой. Я использую функцию unsarsed-text () в XSLT 2.0 для извлечения содержимого из файлов в списке. Однако я изо всех сил пытаюсь правильно сгруппировать вывод XML. Пример желаемого результата:
<collection>
<record>
<title>Interview with Barack Obama</title>
<translatedtitle></translatedtitle>
<watchvideo></watchvideo>
<interviewee>Obama, Barack</interviewee>
<interviewer>Walters, Barbara</interviewer>
<videographer>Smith, John</videographer>
</record>
<record>
<title>Interview with Sarah Palin</title>
<translatedtitle></translatedtitle>
<watchvideo></watchvideo>
<interviewee>Palin, Sarah</interviewee>
<interviewer>Couric, Katie</interviewer>
<videographer>Smith, John</videographer>
</record>
...
</collection>
Прямо сейчас, вот что я получаю:
<collection>
<record>
<title>title</title>
<value>Interview with Barack Obama</value>
<value>Interview with Sarah Palin</value>
<translatedtitle>translatedtitle</translatedtitle>
<value/>
<value/>
<watchvideo>watchvideo</watchvideo>
<value/>
<value/>
<interviewee>interviewee</interviewee>
<value>Obama, Barack</value>
<value>Palin, Sarah</value>
<interviewer>interviewer</interviewer>
<value>Walters, Barbara</value>
<value>Couric, Katie</value>
<videographer>videographer</videographer>
<value>Smith, John</value>
<value>Smith, John </value>
<value/>
<value/>
</record>
</collection>
То есть я не могу сгруппировать вывод по записи. Вот текущий код, с которым я работаю, основанный на примере из книги Дуга Тидвелла по XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="#all" version="2.0">
<xsl:param name="i" select="1"/>
<xsl:param name="increment" select="1"/>
<xsl:param name="operator" select="'<='"/>
<xsl:param name="testVal" select="100"/>
<xsl:template match="/">
<collections>
<collection>
<xsl:for-each select="dumpSet/dump">
<!-- Pull in external tab-delimited files -->
<xsl:for-each select="unparsed-text(concat('../2013-04-26/',@filename),'UTF-8')">
<record>
<!-- Call recursive template to loop through elements. -->
<xsl:call-template name="for-loop">
<xsl:with-param name="i" select="$i"/>
<xsl:with-param name="increment" select="$increment"/>
<xsl:with-param name="operator" select="$operator"/>
<xsl:with-param name="testVal" select="$testVal"/>
</xsl:call-template>
</record>
</xsl:for-each>
</xsl:for-each>
</collection>
</collections>
</xsl:template>
<xsl:template name="for-loop">
<xsl:param name="i"/>
<xsl:param name="increment"/>
<xsl:param name="operator"/>
<xsl:param name="testVal"/>
<xsl:variable name="testPassed">
<xsl:choose>
<xsl:when test="$operator = '<='">
<xsl:if test="$i <= $testVal">
<xsl:text>true</xsl:text>
</xsl:if>
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:if test="$testPassed = 'true'">
<!-- Separate the header from the tab-delimited file. -->
<xsl:for-each select="tokenize(.,'\r|\n')[1]">
<!-- Spit out the field names. -->
<xsl:for-each select="tokenize(.,'\t')[$i]">
<xsl:element name="{replace(lower-case(translate(.,'-.','')),' ','')}">
<xsl:value-of select="replace(lower-case(translate(.,'-.','')),' ','')"/>
</xsl:element>
</xsl:for-each>
</xsl:for-each>
<!-- For the following rows, loop through the field values. -->
<xsl:for-each select="tokenize(.,'\r|\n')[position()>1]">
<xsl:for-each select="tokenize(.,'\t')[$i]">
<value>
<xsl:value-of select="."/>
</value>
</xsl:for-each>
</xsl:for-each>
<!-- Call the template to increment. -->
<xsl:call-template name="for-loop">
<xsl:with-param name="i" select="$i + $increment"/>
<xsl:with-param name="increment" select="$increment"/>
<xsl:with-param name="operator" select="$operator"/>
<xsl:with-param name="testVal" select="$testVal"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Как я должен изменить это, чтобы сгруппировать вывод по записи?
2 ответа
Это может быть проще, если вы используете xsl:analyze-string
разобрать каждую запись. Возможно, есть лучший способ получить имена элементов из заголовка, чем в моем примере, но у меня не было времени думать об этом слишком долго.
Заметки:
Возможно, вам придется изменить кодировку для unparsed-text()
, Я обычно передаю кодировку в качестве параметра, поэтому мне не нужно изменять таблицу стилей. Может быть, кодировка может быть добавлена к <dump/>
?
Было бы неплохо использовать unparsed-text-available()
чтобы увидеть, существует ли файл и может ли быть прочитан с указанной кодировкой.
Кроме того, вы можете выполнить проверку, чтобы убедиться, что значение из заголовка является допустимым QName. Например, если у вас есть апостроф в заголовке, вы получите ошибку. Возможно, было бы лучше использовать имена полей из заголовка в качестве значения атрибута вместо имени элемента. (Подобно: <field name="Interviewee">Obama, Barack</field>
)
Вот мой пример:
Ввод XML
<dumpSet>
<dump filename="file_one.txt"/>
</dumpSet>
file_one.txt
Title Translated Title Watch Video Interviewee Interviewer Videographer
Interview with Barack Obama Obama, Barack Walters, Barbara
Interview with Sarah Palin Palin, Sarah Couric, Katie Smith, John
XSLT 2.0
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="dumpSet">
<collection>
<xsl:apply-templates select="dump[@filename]"/>
</collection>
</xsl:template>
<xsl:template match="dump">
<xsl:variable name="text" select="unparsed-text(@filename, 'iso-8859-1')"/>
<xsl:variable name="header">
<xsl:analyze-string select="$text" regex="(..*)">
<xsl:matching-substring>
<xsl:if test="position()=1">
<xsl:value-of select="regex-group(1)"/>
</xsl:if>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:variable>
<xsl:variable name="headerTokens" select="tokenize($header,'\t')"/>
<xsl:analyze-string select="$text" regex="(..*)">
<xsl:matching-substring>
<xsl:if test="not(position()=1)">
<record>
<xsl:analyze-string select="." regex="([^\t][^\t]*)\t?|\t">
<xsl:matching-substring>
<xsl:variable name="pos" select="position()"/>
<xsl:element name="{replace(normalize-space(lower-case($headerTokens[$pos])),' ','')}">
<xsl:value-of select="normalize-space(regex-group(1))"/>
</xsl:element>
</xsl:matching-substring>
</xsl:analyze-string>
</record>
</xsl:if>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:template>
</xsl:stylesheet>
Выход
<collection>
<record>
<title>Interview with Barack Obama</title>
<translatedtitle/>
<watchvideo/>
<interviewee>Obama, Barack</interviewee>
<interviewer>Walters, Barbara</interviewer>
</record>
<record>
<title>Interview with Sarah Palin</title>
<translatedtitle/>
<watchvideo/>
<interviewee>Palin, Sarah</interviewee>
<interviewer>Couric, Katie</interviewer>
<videographer>Smith, John</videographer>
</record>
</collection>
Пожалуйста, попробуйте этот XSLT, чтобы понять, как вы можете получить желаемое. Вы должны включить вашу функцию перевода, где каждый вам нужно.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<collections>
<collection>
<xsl:for-each select="dumpSet/dump">
<xsl:for-each select="tokenize(unparsed-text(@filename,'UTF-8'),'\n')[not(position()=1)]">
<record>
<title><xsl:value-of select="tokenize(.,'\t')[1]"/></title>
<translatedtitle><xsl:value-of select="tokenize(.,'\t')[2]"/></translatedtitle>
<watchvideo><xsl:value-of select="tokenize(.,'\t')[3]"/></watchvideo>
<interviewee><xsl:value-of select="tokenize(.,'\t')[4]"/></interviewee>
<interviewer><xsl:value-of select="tokenize(.,'\t')[5]"/></interviewer>
<videographer><xsl:value-of select="tokenize(.,'\t')[6]"/></videographer>
</record>
</xsl:for-each>
</xsl:for-each>
</collection>
</collections>
</xsl:template>
</xsl:stylesheet>
выход:
<collections xmlns:xs="http://www.w3.org/2001/XMLSchema">
<collection>
<record>
<title>Interview with Barack Obama</title>
<translatedtitle/>
<watchvideo>Obama, Barack</watchvideo>
<interviewee>Walters, Barbara</interviewee>
<interviewer>
</interviewer>
<videographer/>
</record>
<record>
<title>Interview with Sarah Palin</title>
<translatedtitle/>
<watchvideo>Palin, Sarah</watchvideo>
<interviewee>Couric, Katie</interviewee>
<interviewer>Smith, John</interviewer>
<videographer/>
</record>
</collection>
</collections>