автоматическое создание элементов XML с использованием XSLT - PullRequest
3 голосов
/ 09 декабря 2011

Я хотел бы автоматизировать элементы на основе выражения входного файла.

Мой входной файл выглядит как

<?xml version="1.0" encoding="UTF-8"?>
<mappings>
    <mapping inputContext="InputRoot" outputContext="outputRoot">
        <input>InputParent/InputChild/InputSubChild</input>
        <output>OutputParent/OPChild</output>
    </mapping>
</mappings>

На основе приведенного выше XML я создал приведенный ниже XSLT

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns="http://www.testmapping.org/mapping">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:template match="/">
        <xsl:variable name="outputCtxt" select="mappings/mapping/output"/>
        <xsl:call-template name="contextGenerator">
            <xsl:with-param name="contextPath" select="$outputCtxt"/>
        </xsl:call-template>
    </xsl:template>
    <xsl:template name="contextGenerator">
        <xsl:param name="contextPath" as="xs:string?"/>
        <xsl:variable name="currentContext" select="substring-before($contextPath,'/')"/>
        <xsl:variable name="subContext" select="substring-after($contextPath,'/')"/>
        <xsl:element name="{$currentContext}">
            <xsl:call-template name="contextGenerator">
                <xsl:with-param name="contextPath" select="$subContext"/>
            </xsl:call-template>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

Я ожидаю вывод в формате ниже

<outputRoot>
   <OutputParent>
      <OPChild></OPChild>
   </OutputParent>
</outputRoot>

, когда я пытаюсь преобразовать на основе ввода, я в конечном итоге с ошибкой Ожидаемое QName.Могу ли я получить предложения по решению этой проблемы.

Ответы [ 3 ]

3 голосов
/ 10 декабря 2011

Шаблон contextGenerator неправильно разделяется и рекурсивно. (В аргументе contextGenerator во втором вызове нет /, поэтому разбиение завершается неудачно.)

Добавление следующего к шаблону помогает показать проблему:

<xsl:message>
    [<xsl:value-of select="$currentContext"/>] 
    [<xsl:value-of select="$subContext"/>]
</xsl:message>

Выход:

[OutputParent] 
[OPChild]
[] 
[]

Следующий шаблон замены производит правильный вывод:

<xsl:template name="contextGenerator">
    <xsl:param name="contextPath" as="xs:string?"/>
    <xsl:choose>
        <xsl:when test="contains($contextPath, '/')">
            <xsl:element name="{substring-before($contextPath, '/')}">
                <xsl:variable name="subContext" 
                              select="substring-after($contextPath, '/')"/>
                <xsl:if test="$subContext">
                    <xsl:call-template name="contextGenerator">
                        <xsl:with-param name="contextPath" select="$subContext"/>
                    </xsl:call-template>
                </xsl:if>
            </xsl:element>
        </xsl:when>
        <xsl:otherwise>
            <xsl:element name="{$contextPath}"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

Результат:

<OutputParent>
   <OPChild/>
</OutputParent>
1 голос
/ 10 декабря 2011

Использование 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"
 xmlns:my="my:my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:variable name="vOutNames" select=
  "tokenize(/*/*/output, '/')"/>

 <xsl:template match="/">
  <xsl:sequence select="my:gen($vOutNames)"/>
 </xsl:template>

 <xsl:function name="my:gen" as="element()?">
  <xsl:param name="pNames" as="xs:string*"/>

  <xsl:if test="$pNames[1]">
   <xsl:element name="{$pNames[1]}">
    <xsl:sequence select="my:gen($pNames[position() >1])"/>
   </xsl:element>
  </xsl:if>
 </xsl:function>
</xsl:stylesheet>

, когда это преобразование применяется к предоставленному документу XML :

<mappings>
    <mapping inputContext="InputRoot" outputContext="outputRoot">
        <input>InputParent/InputChild/InputSubChild</input>
        <output>OutputParent/OPChild</output>
    </mapping>
</mappings>

желаемый, правильный результат выдается :

<OutputParent>
   <OPChild/>
</OutputParent>
0 голосов
/ 09 декабря 2011

В XML имена элементов должны соответствовать лексической структуре QNames . Вы пытаетесь создать элемент с именем InputParent/InputChild/InputSubChild. Он содержит символы (/), которые не разрешены в QNames.

Чтобы исправить ошибку, вы можете заменить _ на / ... в зависимости от ваших требований.

например.

    <xsl:element name="{replace($currentContext, '/', '_')}">
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...