基于映射 - xslt拆分字符串

时间:2012-08-20 10:02:00

标签: xml xslt xslt-1.0

如建议的那样。我打破了这个问题here 分成几部分。

我的输入xml表示字符串中存在字段。输入xml最多可包含64个字段元素。输入xml字段元素始终按升序出现。 我的输入xml

<Root>
  <element>field2</element>
  <element>field3</element>
  <element>field21</element>
</Root>

字符串被定义为xslt中的变量。

我的变量

<xsl:variable name="inputstring" select="'013112316145ABC0812345678'"/>

输入xml表示字段2,3和21是字符串中唯一要根据映射xml提取的字段

这是映射xml

<Root>
  <field no="2" charlength="2">variable</field>
  <field no="3" total="4">fixed</field>
  <field no="21" charlength="2">
    <subfield no="1" total="3">fixed</subfield>
    <subfield no="2" charlength="2" idcode="ABC">variable</subfield>
  </field>
  <field no="63" charlength="2">
    <format1>
      <subfield no="1" total="3">fixed</subfield>
    </format1>
    <format2>
      <subfield no="1" total="3">fixed</subfield>
      <subfield no="2" total="7">fixed</subfield>
    </format2>
    <format3>
      <subfield no="1" total="3">fixed</subfield>
      <subfield no="2" total="7">fixed</subfield>
      <subfield no="3" total="6">fixed</subfield>
    </format3>
  </field>
</Root>

映射xml告诉以下

  1. 有四种类型的字段,固定,变量,具有子字段的字段(具有固定和可变)和具有子字段的字段(具有不同的字符)
  2. 字段编号2是变量字段(如上所示),前两个字符(charlength属性)表示字段的长度
  3. 字段3是固定的,总共4个字符。
  4. 字段21是具有子字段(具有固定和可变)的字段,其中前两个字符(charlength)表示字段的字符数
    • 首先发生所有固定的(子字段),然后是变量子字段
    • 其中的子字段始终以idcode开头(对于21的sub,它是ABC),后跟字符长度(charlength属性),然后是子字段。字符的长度也可以是0
    • 出现所有固定和可变字段,长度为0表示没有子字段(在点之上)
  5. 字段63是具有子字段(具有不同的fomats)的字段,取决于字段的长度(charlength属性),可以使用不同的格式
    • 对于字段63,如果长度为03(前两个字符,charlenghth属性),则为格式1.如果为10,则格式为2,如果为16,则为format3
  6. 我想要的输出xml

    <Root>
      <field2>3</field2>
      <!--value is 3 as the charlength is 2(which is 01)-->
      <field3>1123</field3>
      <!--field3 value is 1123 as it is fixed, total length of 4-->
      <field21>
        <subfield1>145</subfield1>
        <!--subfield1 should be 145 as it is fixed length of total 3 chars-->
        <subfield2>12345678</subfield2>
        <!--sufield2 starts with 'ABC', has length 08 chars-->
      </field21>
    </Root>
    

    肖恩编辑。

    击穿

    这是输入和输出之间映射的细分。

    这是我们的字符串变量$ inputstring

    的图片
    '013112316145ABC0812345678'
    

    根据字段定义分为3个字段......

    013    -      1123  -  16145ABC0812345678
     |              |              v  
     v              v           field 21
    field2        field3  
    

    让我们分解字段2:

     01    3
      |    v
      |   payload for field 2. This is output
      v
    Contains the length(1) of the payload, which in this case is '01' = 1
    This length of this 'header' is given by mapping Root/field[@no="2"]/@charlength
    The "2" in this expression comes from the input document node at Root/element .
    

    让分解字段21:

    16   145   ABC0812345678
     |    |       v
     |    |     subfield 2
     |    \ subfield 1
      \
       v
       Header for field 2. Says that the total field 2 length (header + subfield 1 +
    subfield 2 consists of 16 characters. The length for this header was derived from
    the mapping node at Root/field[@no="21"]/@charlength .
    

    最后一个例子:字段21 /子字段2的细分。这是子字段2的图片

    ABC   08   12345678
     |     |     |
     |     |     v
     |     |    This is the payload. It is output as the text node child of output
     |     |      subfield 2
     |     v
     v    Length of the following payload
     Signature. The length and value is equal to the mapping node
       Root/field[@no="21"]/subfield[@no="2"]/@idcode
    

1 个答案:

答案 0 :(得分:2)

嗯.....我说我不会这样做,但无论如何我都是这样做的。

注意事项

  1. 规则5(具有多种格式的复合字段)未实现。这只是太多的工作,所以我将第5条的完成留给你。
  2. 我在www.xmlper.com上测试过,所以我使用了msxsl:node-set()。如果不使用MS,则可能需要稍微调整XSLT引擎。
  3. 通过这么多字符串处理,您应该认真考虑升级到XSLT 2.0。
  4. 样式表

    这个XSLT 1.0样式表...

    <xsl:stylesheet version="1.0"
       exclude-result-prefixes="xsl so msxsl"
       xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
       xmlns:so="http://stackoverflow.com/questions/12035679"
       xmlns:msxsl="urn:schemas-microsoft-com:xslt">
        <xsl:output method="xml" indent="yes" />
        <xsl:strip-space elements="*" />
    
        <xsl:variable name="inputstring" select="'013112316145ABC0812345678'" />
    
        <xsl:variable name="map">
            <so:mapping>
                <field no="2" charlength="2">variable</field>
                <field no="3" total="4">fixed</field>
                <field no="21" charlength="2">
                    <subfield no="1" total="3">fixed</subfield>
                    <subfield no="2" charlength="2" idcode="ABC">variable</subfield>
                </field>
            </so:mapping>
        </xsl:variable>
    
        <xsl:template match="/*">
            <xsl:copy>
                <xsl:call-template name="process-fields">
                    <xsl:with-param name="element-stack" select="element" />
                    <xsl:with-param name="code" select="$inputstring" />
                </xsl:call-template>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template name="process-fields">
            <xsl:param name="element-stack" />
            <xsl:param name="code" />
            <xsl:if test="($code != '') and $element-stack">
                <xsl:variable name="field-no" select="
                  substring-after($element-stack[1],'field')" />
                <xsl:variable name="field-parse-request">
                    <so:field-parse-request code="{$code}">
                        <xsl:copy-of select="msxsl:node-set($map)/so:mapping/
                           field [@no=$field-no]" />
                    </so:field-parse-request>
                </xsl:variable>
                <xsl:variable name="field-parse-result">
                    <xsl:apply-templates
                        select="msxsl:node-set($field-parse-request)/*"
                        mode="field-parse" />
                </xsl:variable>
                <xsl:apply-templates
                       select="msxsl:node-set($field-parse-result)/so:output/*"
                       mode="remove-namespaces" />
                <xsl:call-template name="process-fields">
                    <xsl:with-param name="element-stack"
                       select="$element-stack[position() &gt; 1]" />
                    <xsl:with-param name="code"
                       select="msxsl:node-set($field-parse-result)/
                               so:output[1]/@code" />
                </xsl:call-template>
            </xsl:if>
        </xsl:template>
    
        <xsl:template match="so:field-parse-request[field[subfield]]"
                         mode="field-parse">
            <so:output>
                <xsl:variable name="header"
                      select="substring(@code,1,field/@charlength)" />
                <xsl:attribute name="code">
                    <xsl:value-of
                       select="substring(@code,1+field/@charlength+$header)" />
                </xsl:attribute>
                <xsl:element name="field{field/@no}">
                    <xsl:call-template name="process-subfields">
                        <xsl:with-param name="subfield-stack" select="field/subfield" />
                        <xsl:with-param
                          name="code"
                          select="substring(@code,1+field/@charlength,$header)" />
                    </xsl:call-template>
                </xsl:element>
            </so:output>
        </xsl:template>
    
        <xsl:template match="so:field-parse-request[field[.='variable']]"
                 mode="field-parse">
            <so:output>
                <xsl:variable name="header"
                       select="substring(@code,1,field/@charlength)" />
                <xsl:attribute name="code">
                    <xsl:value-of select="substring(@code,1+field/@charlength+$header)" />
                </xsl:attribute>
                <xsl:element name="field{field/@no}">
                    <xsl:value-of select="substring(@code,1+field/@charlength,$header)" />
                </xsl:element>
            </so:output>
        </xsl:template>
    
        <xsl:template match="so:field-parse-request[subfield[.='variable']]"
                mode="field-parse">
            <so:output>
                <xsl:variable name="header"
                   select="substring( @code,
                                      1 + string-length( subfield/@idcode),
                                      subfield/@charlength)" />
                <xsl:attribute name="code">
                    <xsl:value-of select="substring(
                         @code,
                         1 + string-length( subfield/@idcode) +
                             subfield/@charlength + $header)" />
                </xsl:attribute>
                <xsl:element name="subfield{subfield/@no}">
                    <xsl:value-of select="
                       substring( @code, 
                         1 + string-length( subfield/@idcode) +
                           subfield/@charlength, $header)" />
                </xsl:element>
            </so:output>
        </xsl:template>
    
        <xsl:template match="so:field-parse-request[ field[.='fixed']] | so:field-parse-request[subfield[.='fixed']]" mode="field-parse">
            <so:output>
                <xsl:attribute name="code">
                    <xsl:value-of select="substring(@code, (field/@total | subfield/@total) + 1)" />
                </xsl:attribute>
                <xsl:element name="{concat( name(field|subfield) ,field/@no | subfield/@no)}">
                    <xsl:value-of select="substring(@code,1,field/@total | subfield/@total)" />
                </xsl:element>
            </so:output>
        </xsl:template>
    
        <xsl:template name="process-subfields">
            <xsl:param name="subfield-stack" />
            <xsl:param name="code" />
            <xsl:if test="($code != '') and $subfield-stack">
                <xsl:variable name="active-subfield-index">
                    <xsl:choose>
                        <xsl:when test="not( $subfield-stack[1]/@idcode)">1</xsl:when>
                        <xsl:otherwise>
                            <xsl:value-of select="
                               count($subfield-stack
                                [starts-with($code,@idcode)]/preceding-sibling::*)+1" />
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:variable>
                <xsl:variable name="field-parse-request">
                    <so:field-parse-request code="{$code}">
                        <xsl:copy-of select="$subfield-stack[$active-subfield-index]" />
                    </so:field-parse-request>
                </xsl:variable>
                <xsl:variable name="field-parse-result">
                    <xsl:apply-templates
                      select="msxsl:node-set($field-parse-request)/*"
                      mode="field-parse" />
                </xsl:variable>
                <xsl:apply-templates
                    select="msxsl:node-set($field-parse-result)/so:output/*"
                    mode="remove-namespaces" />
                <xsl:call-template name="process-subfields">
                    <xsl:with-param name="subfield-stack"
                      select="$subfield-stack[position() != $active-subfield-index]" />
                    <xsl:with-param name="code"
                       select="msxsl:node-set($field-parse-result)/ 
                               so:output[1]/@code" />
                </xsl:call-template>
            </xsl:if>
        </xsl:template>
    
        <xsl:template match="*" mode="remove-namespaces">
            <xsl:element name="{local-name(.)}">
                <xsl:apply-templates select="@*|node()" mode="remove-namespaces" />
            </xsl:element>
        </xsl:template>
    
        <xsl:template match="@*|text()" mode="remove-namespaces">
            <xsl:copy />
        </xsl:template>
    
    </xsl:stylesheet>
    

    输入

    ...将采用此输入文档......

    <Root>
      <element>field2</element>
      <element>field3</element>
      <element>field21</element>
    </Root>
    

    输出

    ...并根据规则5规定的所有规则对其进行转换,并生成此输出...

    <Root>
      <field2>3</field2>
      <field3>1123</field3>
      <field21>
        <subfield1>145</subfield1>
        <subfield2>12345678</subfield2>
      </field21>
    </Root>