Get the value after the colon - keys

本秂侑毒 提交于 2019-12-13 03:38:57

问题


I have almost the same issue for this one: Get value after each last colon. In my case, I need to iterate for every occurrence of :A: and :B:, :B: is a child of :A:. In my code, I am using a call-template which is I'm not very familiar with. But, I need to explore the other functionalities/elements in xslt.

Here is my sample test file:

<Record>
:A:This is sample only 1
:B:This is sample only 2
:B:This is sample only 3
:A:This is sample only 4
:B:This is sample only 5
</Record>

Expected output:

<Record>
  <Detail>
    <FieldA>This is sample only 1</FieldA>
    <Trans>
      <Group>
        <FieldB>This is sample only 2</FieldB>
      </Group>
      <Group>
        <FieldB>This is sample only 3</FieldB>
      </Group>
    </Trans>
  </Detail>
  <Detail>
    <FieldA>This is sample only 4</FieldA>
    <Trans>
      <Group>
        <FieldB>This is sample only 5</FieldB>
      </Group>
    </Trans>
  </Detail>
<Record>

For every occurrence of :A:, I need to create a <Detail> record, then for each :B: will create a <Group> record. And here is my XSLT code,

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" version="1.0" indent="yes"/>
<xsl:template match="node()|@*">
    <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
</xsl:template>
<xsl:template match="Record">
    <xsl:call-template name="FormatXML">
        <xsl:with-param name="Input">
            <Record>
            <xsl:for-each select="tokenize(.,':A:')">
                <xsl:analyze-string select="." regex=":([0-9A-Za-z]+):(.*)\n">
                    <xsl:matching-substring>
                        <xsl:variable name="FieldA">
                            <xsl:if test="regex-group(1) = 'A'">
                                <FieldB>
                                    <xsl:value-of select="regex-group(2)"/>
                                </FieldB>
                            </xsl:if> 
                        </xsl:variable>
                        <xsl:for-each select="tokenize(.,':B:')">
                        <xsl:variable name="FieldB">
                            <xsl:if test="regex-group(1) = 'B'">
                                <FieldB>
                                    <xsl:value-of select="regex-group(2)"/>
                                </FieldB>
                            </xsl:if> 
                        </xsl:variable>
                        <Group>
                            <FieldB>
                                <xsl:value-of select="$FieldB"/>
                            </FieldB>
                        </Group>
                        </xsl:for-each>
                        <Detail>
                            <FieldA>
                                <xsl:value-of select="$FieldA"/>
                            </FieldA>
                        </Detail>
                    </xsl:matching-substring>
                </xsl:analyze-string>                
            </xsl:for-each>
            </Record>
        </xsl:with-param>
    </xsl:call-template>
</xsl:template>
<xsl:template name="FormatXML">
    <xsl:param name="Input"/>
    <xsl:apply-templates select="$Input"/>    
</xsl:template>
<xsl:template match="/Record">
    <xsl:copy>
        <xsl:apply-templates select="Detail"/>
    </xsl:copy>
</xsl:template>
<xsl:template match="Detail">
    <xsl:copy>
        <xsl:copy-of select="FieldA"/>
        <Trans>
            <xsl:apply-templates select="Group"/>
        </Trans>
    </xsl:copy>
</xsl:template>
<xsl:template match="Group">
    <xsl:copy>
        <xsl:apply-templates select="FieldB"/>
    </xsl:copy>
</xsl:template>

My xslt code is not working. Can anyone help me if what I missed in my code. Your feedback is highly appreciated.

Thanks!


回答1:


I would suggest a radically different approach: first convert the string to individual elements, so that you have:

<FieldA>This is sample only 1</FieldA>
<FieldB>This is sample only 2</FieldB>
<FieldB>This is sample only 3</FieldB>
<FieldA>This is sample only 4</FieldA>
<FieldB>This is sample only 5</FieldB>

in a variable.

Then group the elements in the variable and output them the way you want them:

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:template match="Record">
    <!-- convert strings to elements -->
    <xsl:variable name="temp" as="element()*">
        <xsl:analyze-string select="." regex="^:([AB]):(.*)$" flags="m">
            <xsl:matching-substring>
                <xsl:element name="Field{regex-group(1)}">
                    <xsl:value-of select="regex-group(2)" />
                </xsl:element>
            </xsl:matching-substring>
        </xsl:analyze-string>
    </xsl:variable>
    <!-- output  -->
    <xsl:copy>
        <!-- group elements -->
        <xsl:for-each-group select="$temp" group-starting-with="FieldA">
            <Detail>
                <xsl:copy-of select="current-group()[1]" />
                <Trans>
                    <xsl:for-each select="current-group()[position() ge 2]">
                        <Group>
                            <xsl:copy-of select="." />
                        </Group>
                    </xsl:for-each>
                </Trans>
            </Detail>
        </xsl:for-each-group>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Demo: http://xsltransform.net/gVhD8Ro/1




回答2:


With XSLT 3.0 (as supported by Saxon 9.8 all editions) or Altova XMLSpy and Raptor you could directly group the lines:

<?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"
    expand-text="true" version="3.0">

    <xsl:output indent="yes"/>

    <xsl:template match="Record">
        <xsl:copy>
            <xsl:for-each-group select="tokenize(., '\r?\n')[normalize-space()]"
                group-starting-with=".[starts-with(., ':A:')]">
                <Detail>
                    <FieldA>{replace(., ':A:', '')}</FieldA>
                    <Trans>
                        <xsl:apply-templates select="current-group()[position() gt 1]"/>
                    </Trans>
                </Detail>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>

    <xsl:template match=".[starts-with(., ':B:')]">
        <Group>
            <FieldB>{replace(., ':B:', '')}</FieldB>
        </Group>
    </xsl:template>

</xsl:stylesheet>


来源:https://stackoverflow.com/questions/45539458/get-the-value-after-the-colon-keys

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!