I have the following node I need to parse using XSLT 1.0 from xml file
<log>Passed -ID:1 -Log:
Passed -ID:2 -Log:Suite
File/Folder
Failed -ID:3 -Log:Suite
Validate Install Failed
Passed -ID:4 -Log:
</log>
Here is the -ID: -Log:
as you can see can be written on one line or on multiple lines.
In result I would like to get another xml file where the data from node will be parsed. If record with ID was Passed then I need to write "/>. If record was Failed then I need to write
<testcase name="<ID Name>">
<failure message="<Log Message>"/>
</testcase>
In other words I need to get this xml file.
<xml>
<testcase name="1"/>
<testcase name="2"/>
<开发者_高级运维;testcase name="3">
<failure message="Suite Validate Install Failed"/>
</testcase>
<testcase name="4"/>
</xml>
What do you think can be best way to do this?
The xml file is actually very big and I provided here only one node I need to parse. I'm using xslt because I'm getting other information from other nodes which I also need for result xml files.
Thank you.
XSLT is not the right technology for this task. XSLT is fantastic at transforming the structure of XML documents (typically to another XML document, but XML-to-text is also possible). XSLT is not good for parsing text and manipulating it.
What you have is some structured text that happens to be within an XML element.
I would opt for another transformation technique, Regex, or simple string parsing methods.
The following XSLT demonstrates how to split log
content in tokens just using tokenize()
. There are probably better choices with XSLT 2.0 (for example xsl:analyze-string
), but because of using tokenize()
only, this solution is applicable also to XSLT 1.0 extended with EXSLT templates.
XSLT 2.0 tested on Saxon-B 9.0.0.2J
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xml>
<xsl:variable name="string" select="."/>
<xsl:variable name="pass" select="tokenize($string,'Passed -ID:')[not(position()=1)]"/>
<xsl:for-each select="$pass">
<xsl:choose>
<xsl:when test="contains(.,'Failed -ID:')">
<xsl:variable name="failure" select="tokenize(.,'Failed -ID:')"/>
<xsl:for-each select="$failure">
<xsl:choose>
<xsl:when test="position()=1">
<testcase name="{tokenize(.,'\s-Log:')[1]}"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="tc" select="tokenize(.,'\s-Log:')"/>
<testcase name="{$tc[1]}">
<failure message="{$tc[2]}"/>
</testcase>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<testcase name="{tokenize(.,'\s-Log:')[1]}"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:apply-templates/>
</xml>
</xsl:template>
<xsl:template match="log"/>
</xsl:stylesheet>
The above XSLT applied on the following input:
<log>Passed -ID:1 -Log:
Passed -ID:2 -Log:Suite
File/Folder
Failed -ID:3 -Log:Suite
Validate Install Failed
Passed -ID:4 -Log:
Failed -ID:5 -Log:aaaaaa
Failed -ID:6 -Log:dfsfsdf
Failed -ID:7 -Log:dsfsfs
fsdfsdfsdfsdfs
Passed -ID:8 -Log:dfsdfsf
Failed -ID:9 -Log:dfsdfs
</log>
Produces the following output:
<xml>
<testcase name="1"/>
<testcase name="2"/>
<testcase name="3">
<failure message="Suite
Validate Install Failed
"/>
</testcase>
<testcase name="4"/>
<testcase name="5">
<failure message="aaaaaa
"/>
</testcase>
<testcase name="6">
<failure message="dfsfsdf
"/>
</testcase>
<testcase name="7">
<failure message="dsfsfs
fsdfsdfsdfsdfs
"/>
</testcase>
<testcase name="8"/>
<testcase name="9">
<failure message="dfsdfs
"/>
</testcase>
</xml>
Note that 

is due to line-feeds of the source text appearing because we are placing the content inside the attribute value. To get rid of that it would be better to include the message as content of the element failure
. Anyway the following article deals with tricky spaces.
精彩评论