在每个最后一个冒号后获取值

时间:2017-08-05 16:06:51

标签: xml xslt xslt-2.0

我需要在最后一次冒号后得到每个数据的值。例如,我有这个文件:

<Data>
:20:PmtReferenceID000012
:21:Not used
:25: PHMNLBICXXX/Account00010203
:28c:00001/0001 (The 'c' in :28 can be either in upper or lower case)

:20:PmtReferenceID000012
:21:Not used
:25: PHMNLBICXXX/Account00010203
:28c:00001/0001 (The 'c' in :28 can be either in upper or lower case)
</Data>

我需要将':20:'后的值存储到<ABCD>,':21:'到<EFGH>,':25:'到<IJKL>和':28c :'到<MNOP>

这是我的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">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>
<xsl:template match="Data">
    <Data>
        <xsl:variable name="OneLine" select="replace(translate(.,'&#10;', '|'),'&#xD;','')"/>
        <ABCD>
            <xsl:value-of select="substring-before(substring-after($OneLine, ':20:'),'|:')"/>
        </ABCD>
        <EFGH>
            <xsl:value-of select="substring-before(substring-after($OneLine, ':21:'),'|:')"/>
        </EFGH>
        <IJKL>
            <xsl:value-of select="substring-before(substring-after($OneLine, ':25:'),'|:')"/>
        </IJKL>
        <MNOP>
            <xsl:value-of select="substring-before(substring-after($OneLine, ':28c:'),'|:')"/>
        </MNOP>
    </Data>
</xsl:template>

预期产出:

<Data>
   <ABCD>PmtReferenceID000012</ABCD>
   <EFGH>Not used</EFGH>
   <IJKL> PHMNLBICXXX/Account00010203</IJKL>
   <MNOP>00001/0001</MNOP>
</Data>
<Data>
   <ABCD>PmtReferenceID000012</ABCD>
   <EFGH>Not used</EFGH>
   <IJKL> PHMNLBICXXX/Account00010203</IJKL>
   <MNOP>00001/0001</MNOP>
</Data>

我做的是,我首先将回车替换为管道('|'),这样,如果我得到的值例如':20:',我将寻找'|'并在':20:'之后和'|'之前对值进行子串。有没有一个简单的方法我会在每个最后一个冒号之后获得值,因为有很多键,如果我要使用我做的方法?我正在考虑使用索引或位置,并存储所有键(:20:,:21:,:25:,:28c'),以便如果下一条记录包含':21:'或':25 :'或':28c',它将获得该键之前的值。但是,我不知道如何使用xslt。

非常感谢您的反馈!

谢谢,

3 个答案:

答案 0 :(得分:3)

在XSLT 3.0中,您可以为不同的字符串编写模板,例如

<?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"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math" exclude-result-prefixes="xs math"
    version="3.0">

    <xsl:output indent="yes"/>

    <xsl:template match="Data">
        <xsl:copy>
            <xsl:apply-templates select="tokenize(., '\r?\n')[normalize-space()]"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match=".[. instance of xs:string and matches(., '^:20:')]">
        <ABCD>
            <xsl:value-of select="replace(., '^:20:', '')"/>
        </ABCD>
    </xsl:template>

    <xsl:template match=".[. instance of xs:string and matches(., '^:21:')]">
        <EFGH>
            <xsl:value-of select="replace(., '^:21:', '')"/>
        </EFGH>
    </xsl:template>

    <xsl:template match=".[. instance of xs:string and matches(., '^:25:')]">
        <IJKL>
            <xsl:value-of select="replace(., '^:25:', '')"/>
        </IJKL>
    </xsl:template>

    <xsl:template match=".[. instance of xs:string and matches(., '^:28c:', 'i')]">
        <MNOP>
            <xsl:value-of select="replace(., '^:28c:', '', 'i')"/>
        </MNOP>
    </xsl:template>    
</xsl:stylesheet>

使用Saxon 9.8或Altova XMLSpy / Raptor完成工作和输出

<Data>
   <ABCD>PmtReferenceID000012</ABCD>
   <EFGH>Not used</EFGH>
   <IJKL> PHMNLBICXXX/Account00010203</IJKL>
   <MNOP>00001/0001</MNOP>
</Data>

(输入

<Data>
:20:PmtReferenceID000012
:21:Not used
:25: PHMNLBICXXX/Account00010203
:28c:00001/0001
</Data>

作为替代方案,您可以使用analyze-string函数并匹配返回的fn:match元素,而不是标记和处理字符串:

<?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"
    xmlns:fn="http://www.w3.org/2005/xpath-functions"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    exclude-result-prefixes="xs fn math"
    version="3.0">

    <xsl:output indent="yes"/>

    <xsl:template match="Data">
        <xsl:copy>
            <xsl:apply-templates select="analyze-string(., '^(:[0-9]+[a-z]*:)(.*)\r?\n', 'im')//fn:match"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="fn:match[fn:group[@nr = 1][. = ':20:']]">
        <ABCD>
            <xsl:value-of select="fn:group[@nr = 2]"/>
        </ABCD>
    </xsl:template>

    <xsl:template match="fn:match[fn:group[@nr = 1][. = ':21:']]">
        <EFGH>
            <xsl:value-of select="fn:group[@nr = 2]"/>
        </EFGH>
    </xsl:template>

    <xsl:template match="fn:match[fn:group[@nr = 1][. = ':25:']]">
        <IJKL>
            <xsl:value-of select="fn:group[@nr = 2]"/>
        </IJKL>
    </xsl:template>

    <xsl:template match="fn:match[fn:group[@nr = 1][matches(., '^:28c:', 'i')]]">
        <MNOP>
            <xsl:value-of select="fn:group[@nr = 2]"/>
        </MNOP>
    </xsl:template>

</xsl:stylesheet>

最后,采用map参数的思想来定义元素名称,第二个解决方案可以缩短为

<?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"
    xmlns:fn="http://www.w3.org/2005/xpath-functions"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    exclude-result-prefixes="xs fn math"
    version="3.0">

    <xsl:param name="map" as="map(xs:string, xs:string)"
        select="map {
                  '20' : 'ABCD',
                  '21' : 'EFGH',
                  '25' : 'IJKL',
                  '28c' : 'MNOP'
                }"/>

    <xsl:output indent="yes"/>

    <xsl:template match="Data">
        <xsl:copy>
            <xsl:apply-templates select="analyze-string(., '^(:([0-9]+[a-z]*):)(.*)\r?\n', 'im')//fn:match" mode="wrap"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="match" mode="wrap" xpath-default-namespace="http://www.w3.org/2005/xpath-functions">
        <xsl:element name="{$map(lower-case(.//group[@nr = 2]))}">
            <xsl:value-of select="group[@nr = 3]"/>
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

答案 1 :(得分:2)

  

我是否有一个简单的方法可以在每个最后一个冒号之后得到值,因为有太多的键[...]

是。您可以使用RegEx匹配 在以下模板中,regex-group(2)包含第二个/(最后一个)冒号后面的字符串。 regex-group(1)包含密钥。

<xsl:template match="Data">
    <Data>
        <xsl:analyze-string select="." regex=":([0-9A-Za-z]+):(.*)\n">
            <xsl:matching-substring>
                (<xsl:value-of select="regex-group(1)" /> --- <xsl:value-of select="regex-group(2)" />)<xsl:text>&#xa;</xsl:text>
            </xsl:matching-substring>
        </xsl:analyze-string>
    </Data>
</xsl:template>

部分输出:

(20 --- PmtReferenceID000012)
(21 --- Not used)
(25 ---  PHMNLBICXXX/Account00010203)
(28c --- 00001/0001 (The 'c' in :28 can be either in upper or lower case))

通过它,您可以创建一个键/值Dictionary,用于在文本周围创建标签。

像这样:

  • Key:20,Value:ABCD
  • Key:21,Value:EFGH
  • ...

例如:您可以在XSL文件中创建一个变量来存储映射:

<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:map="http://custom.map">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

  <xsl:variable name="Mapping">
    <Map key="20">ABCD</Map>
    <Map key="21">EFGH</Map>
    <Map key="25">IJKL</Map>
    <Map key="28c">MNOP</Map>
  </xsl:variable>

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

  <xsl:template match="Data">
    <Data>
        <xsl:analyze-string select="." regex=":([0-9A-Za-z]+):(.*)\n">
            <xsl:matching-substring>
                <xsl:element name="{$Mapping/Map[@key=regex-group(1)]/text()}"><xsl:value-of select="regex-group(2)" /></xsl:element>
            </xsl:matching-substring>
        </xsl:analyze-string>
    </Data>
  </xsl:template>
</xsl:stylesheet>

完整输出:

<?xml version="1.0" encoding="UTF-8"?>
<Data xmlns:xs="http://www.w3.org/2001/XMLSchema"
      xmlns:fn="http://www.w3.org/2005/xpath-functions"
      xmlns:map="http://custom.map">
   <ABCD>PmtReferenceID000012</ABCD>
   <EFGH>Not used</EFGH>
   <IJKL> PHMNLBICXXX/Account00010203</IJKL>
   <MNOP>00001/0001 (The 'c' in :28 can be either in upper or lower case)</MNOP>
</Data>

或者你可以直接外包映射并为它们创建一个单独的文件......

答案 2 :(得分:0)

我在原帖中写了这个答案,但没有发布,因为它基本上与zx485发布的那个相似。

但是,我仍然建议使用key来检索相应的元素名称(我也认为正则表达式可以更简单,更健壮)。

我添加了一个标记步骤,将数据拆分为每个双换行字符的单独<Data>包装。

XSLT 2.0

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

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

<xsl:variable name="map">
    <name key="20">ABCD</name>
    <name key="21">EFGH</name>
    <name key="25">IJKL</name>
    <name key="28C">MNOP</name>
</xsl:variable>

<xsl:key name="nm" match="name" use="@key" />

<xsl:template match="Data">
    <xsl:for-each select="tokenize(., '\n\n')">
        <Data>
            <xsl:analyze-string select="." regex="^:([^:]*):(.*)$" flags="m">
                <xsl:matching-substring>
                    <xsl:element name="{key('nm', upper-case(regex-group(1)), $map)}">
                        <xsl:value-of select="regex-group(2)" />
                    </xsl:element>
                </xsl:matching-substring>
            </xsl:analyze-string>
        </Data>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

演示:http://xsltransform.net/ehVYZNm