xslt 2.0 токенизировать и группировать

У меня есть текстовый файл со следующими данными:

<t>Heros
Firstname Sean
Lastname Connery
DOB 25-08-1930

Films
Dr.No 1962
Goldfinger 1964
Thunerball 1965

Award
name Academy
time 1

Award
name BAFTA
time 2

Award
name Gloden Globes
time 3</t>

Ожидаемый результат должен выглядеть следующим образом:

<Jamesfilms>
    <heros>
        <firstName>Sean</firstName>
        <lastName>Connery</lastName>
        <DOB>25-08-1930</DOB>
    </heros>
    <films>
        <Dr.No>1962</Dr.No>
        <Goldfinger>1964</Goldfinger>
        <Thunerball>1965</Thunerball>
    </films>
    <award>
        <name>Academy</name>
        <times>1</times>
    </award>
    <award>
        <name>BAFTA</name>
        <times>2</times>
    </award>
    <award>
        <name>Gloden Globes</name>
        <times>3</times>
    </award>
</Jamesfilms>

содержимое текстового файла - это пары значений ключей разделителя пространства, как разделить значения ключа и сгенерировать узел XML?

РЕДАКТИРОВАТЬ: Я попытался ответить Дэниел Хейли, и пытается разрешить следующие исключения:

Error at xsl:for-each on line 10 of transformer.xslt:
  XTDE1170: Invalid relative URI: Illegal character in path at index 5: 

Класс Java:

    final String TXT_PATH = "E:/tmp/test/input.txt";
    final String XSLT_PATH = "E:/tmp/test/txtToXml.xslt";
    final String XML_PATH = "E:/tmp/test/test_xml_result.xml";

    TransformerFactory tFactory = new net.sf.saxon.TransformerFactoryImpl();
    Transformer transformer = tFactory.newTransformer(new StreamSource(new File(XSLT_PATH)));
    transformer.transform(new StreamSource(new File(TXT_PATH)),new StreamResult(new File(XML_PATH)));

и модифицированный xslt:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:param name="input-encoding" as="xs:string" select="'iso-8859-1'"/>

  <xsl:variable name="initData" as="node()">
    <Jamesfilms>
      <xsl:for-each select="tokenize(unparsed-text(., $input-encoding),'\r?\n\r?\n')">
        <xsl:variable name="tokens" select="tokenize(.,'\r?\n')"/>
        <xsl:choose>
          <xsl:when test="$tokens[1] castable as xs:QName">
            <xsl:element name="{$tokens[1]}">
              <xsl:for-each select="$tokens[position() > 1]">
                <xsl:variable name="tokens2" select="tokenize(.,'\s')"/>
                <xsl:choose>
                  <xsl:when test="$tokens2[1] castable as xs:QName">
                    <xsl:element name="{$tokens2[1]}">
                      <xsl:value-of select="$tokens2[position()>1]" separator=" "/>
                    </xsl:element>                      
                  </xsl:when>
                  <xsl:otherwise>
                    <xsl:message terminate="yes">Invalid element name: <xsl:value-of select="$tokens2[1]"/></xsl:message>
                  </xsl:otherwise>
                </xsl:choose>
              </xsl:for-each>
            </xsl:element>            
          </xsl:when>
          <xsl:otherwise>
            <xsl:message terminate="yes">Invalid element name: <xsl:value-of select="$tokens[1]"/></xsl:message>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each>
    </Jamesfilms>
  </xsl:variable>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/">
    <xsl:apply-templates select="$initData"/>    
  </xsl:template>

  <!--Add additional templates to do further transforming of the initial data ($initData).-->

</xsl:stylesheet>

1 ответ

Решение

Вам не нужно группировать; Вы можете просто токенизировать (и токенизировать и токенизировать...).

Вот пример. Это ничего не делает с регистром имен элементов. Вы можете справиться с этими изменениями во время создания $initDataИли вы можете добавить дополнительные шаблоны для обработки любых изменений.

Кроме того, имена элементов должны быть действительными QNames. В данный момент таблица стилей завершает обработку сообщением, но вы можете изменить способ его обработки.

Это должно по крайней мере, чтобы вы начали...

XSLT 2.0

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:param name="input-encoding" as="xs:string" select="'iso-8859-1'"/>
  <xsl:param name="input-uri" as="xs:string" select="'so.txt'"/>

  <xsl:variable name="initData" as="node()">
    <Jamesfilms>
      <xsl:for-each select="tokenize(unparsed-text($input-uri, $input-encoding),'\r?\n\r?\n')">
        <xsl:variable name="tokens" select="tokenize(.,'\r?\n')"/>
        <xsl:choose>
          <xsl:when test="$tokens[1] castable as xs:QName">
            <xsl:element name="{$tokens[1]}">
              <xsl:for-each select="$tokens[position() > 1]">
                <xsl:variable name="tokens2" select="tokenize(.,'\s')"/>
                <xsl:choose>
                  <xsl:when test="$tokens2[1] castable as xs:QName">
                    <xsl:element name="{$tokens2[1]}">
                      <xsl:value-of select="$tokens2[position()>1]" separator=" "/>
                    </xsl:element>                      
                  </xsl:when>
                  <xsl:otherwise>
                    <xsl:message terminate="yes">Invalid element name: <xsl:value-of select="$tokens2[1]"/></xsl:message>
                  </xsl:otherwise>
                </xsl:choose>
              </xsl:for-each>
            </xsl:element>            
          </xsl:when>
          <xsl:otherwise>
            <xsl:message terminate="yes">Invalid element name: <xsl:value-of select="$tokens[1]"/></xsl:message>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each>
    </Jamesfilms>
  </xsl:variable>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/">
    <xsl:apply-templates select="$initData"/>    
  </xsl:template>

  <!--Add additional templates to do further transforming of the initial data ($initData).-->

</xsl:stylesheet>

РЕДАКТИРОВАТЬ

Вы передаете текстовый файл в качестве входных данных преобразования. Вот почему вы должны были добавить <t> элемент.

Поскольку у вас фактически нет ввода XML, вы можете передать саму таблицу стилей в качестве ввода. Ничто не будет обработано, потому что мы применяем шаблоны только к переменной в шаблоне, который соответствует root (/).

Вам также необходимо установить input-uri параметр с transformer.setParameter("input-uri", TXT_PATH);, Если ваш путь абсолютный, обязательно добавьте file:/// протокол.

Пример...

Текстовый файл

Heros
Firstname Sean
Lastname Connery
DOB 25-08-1930

Films
Dr.No 1962
Goldfinger 1964
Thunerball 1965

Award
name Academy
time 1

Award
name BAFTA
time 2

Award
name Gloden Globes
time 3

Java (вам нужно изменить пути / имена файлов)

final String TXT_PATH = "file:///C:/tmp/input.txt";
final String XSLT_PATH = "C:/tmp/txt2xml.xsl";
final String XML_PATH = "C:/tmp/test_xml_result.xml";

TransformerFactory tFactory = new net.sf.saxon.TransformerFactoryImpl();
Transformer transformer = tFactory.newTransformer(new StreamSource(new File(XSLT_PATH)));
transformer.setParameter("input-uri", TXT_PATH);
transformer.transform(new StreamSource(new File(XSLT_PATH)),new StreamResult(new File(XML_PATH)));

XSLT 2.0

То же, что и выше.

Выход

<Jamesfilms>
   <Heros>
      <Firstname>Sean</Firstname>
      <Lastname>Connery</Lastname>
      <DOB>25-08-1930</DOB>
   </Heros>
   <Films>
      <Dr.No>1962</Dr.No>
      <Goldfinger>1964</Goldfinger>
      <Thunerball>1965</Thunerball>
   </Films>
   <Award>
      <name>Academy</name>
      <time>1</time>
   </Award>
   <Award>
      <name>BAFTA</name>
      <time>2</time>
   </Award>
   <Award>
      <name>Gloden Globes</name>
      <time>3</time>
   </Award>
</Jamesfilms>

Однако, так как вы используете Saxon, вы можете использовать s9api и указать начальный шаблон. Так я бы сделал это вместо передачи таблицы стилей в качестве входных данных для преобразования.

Пример...

Джава

final String TXT_PATH = "file:///C:/tmp/input.txt";
final String XSLT_PATH = "C:/tmp/txt2xml.xsl";
final String XML_PATH = "C:/tmp/test_xml_result.xml";

Processor processor = new Processor(false);
Serializer serializer = processor.newSerializer();
serializer.setOutputFile(new File(XML_PATH));
XsltCompiler compiler = processor.newXsltCompiler();
XsltExecutable executable = compiler.compile(new StreamSource(new File(XSLT_PATH)));
XsltTransformer transformer = executable.load();
transformer.setInitialTemplate(new QName("root"));
transformer.setParameter(new QName("input-uri"), new XdmAtomicValue(TXT_PATH));
transformer.setDestination(serializer);
transformer.transform();

XSLT 2.0

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:param name="input-encoding" as="xs:string" select="'iso-8859-1'"/>
  <xsl:param name="input-uri" as="xs:string"/>

  <xsl:variable name="initData" as="node()">
    <Jamesfilms>
      <xsl:for-each select="tokenize(unparsed-text($input-uri, $input-encoding),'\r?\n\r?\n')">
        <xsl:variable name="tokens" select="tokenize(.,'\r?\n')"/>
        <xsl:choose>
          <xsl:when test="$tokens[1] castable as xs:QName">
            <xsl:element name="{replace($tokens[1],'\s','')}">
              <xsl:for-each select="$tokens[position() > 1]">
                <xsl:variable name="tokens2" select="tokenize(.,'\s')"/>
                <xsl:choose>
                  <xsl:when test="$tokens2[1] castable as xs:QName">
                    <xsl:element name="{$tokens2[1]}">
                      <xsl:value-of select="$tokens2[position()>1]" separator=" "/>
                    </xsl:element>                      
                  </xsl:when>
                  <xsl:otherwise>
                    <xsl:message terminate="yes">Invalid element name: <xsl:value-of select="$tokens2[1]"/></xsl:message>
                  </xsl:otherwise>
                </xsl:choose>
              </xsl:for-each>
            </xsl:element>            
          </xsl:when>
          <xsl:otherwise>
            <xsl:message terminate="yes">Invalid element name: <xsl:value-of select="$tokens[1]"/></xsl:message>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each>
    </Jamesfilms>
  </xsl:variable>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/" name="root">
    <xsl:apply-templates select="$initData"/>    
  </xsl:template>

  <!--Add additional templates to do further transforming of the initial data ($initData).-->

</xsl:stylesheet>

Вход и выход будут одинаковыми. Дайте мне знать, если вы хотите, чтобы я добавил импорт java в пример.

Другие вопросы по тегам