使用XSLT自动创建xml元素

时间:2011-12-09 19:02:37

标签: xml xslt xpath

我想基于输入文件表达式自动化元素。

我的输入文件类似于

<?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 个答案:

答案 0 :(得分:3)

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 :(得分:1)

使用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>

答案 2 :(得分:0)

在XML中,元素名称必须符合QNames的词法结构。您正在尝试创建名为InputParent/InputChild/InputSubChild的元素。这包含QNames中不允许的字符(/)。

要修正错误,您可以将_替换为/ ...取决于您的要求。

E.g。

    <xsl:element name="{replace($currentContext, '/', '_')}">