xslt find and insert and or replace with incremental number
I have a list of terms in xml. Terms either have termtype nd or pt. If a term is termtype nd, it has no ID, but instead references a termtype PT which does have an ID. I need to do two things.
replace the XXX for termId value with开发者_StackOverflow中文版 an incremental number for termtype nd, so all terms have an id.
where a termType ND references a termType PT, I need to somehow look for its ID and insert it:
<Zthes>
<term>
<termName>Term 1</termName>
<termId>insert new term id N+1 </termId>
<termType>Nd</termType>
<relation>
<relationType>USE</relationType>
<termId>insert ID of term 2 <termID>
<termName>Term2</termName>
</relation>
</term>
<term>
<termName>Term 2</termName>
<termId>587889</termId>
<termType>pt</termType>
</term>
</zthes>
Could anyone give me pointers on how to do this. I don't know anything about xslt. I've looked on here and found stuff about find and replace with Unique but from what I understand of the answer, I don't think it is applicable.
ALso, It doesn't have to be xslt. I work with a developer who said that this was the best way to do this, but that the answer was too trivial for him to bother with. Its too difficult for me though!
This single transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="ktermPtIdByName"
match="term[termType='pt']/termId"
use="../termName"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="term[termType='Nd']/termId">
<termId>
<xsl:number level="single" count="/*/term[termType='Nd']"/>
</termId>
</xsl:template>
<xsl:template match="relation/termId">
<termId>
<xsl:value-of select="key('ktermPtIdByName', ../termName)"/>
</termId>
</xsl:template>
</xsl:stylesheet>
when applied on the following XML document (your XML but corrected to be syntactically well-formed and semantically correct, then enlarged twice to be made more interesting):
<zthes>
<term>
<termName>Term 1</termName>
<termId></termId>
<termType>Nd</termType>
<relation>
<relationType>USE</relationType>
<termId></termId>
<termName>Term 2</termName>
</relation>
</term>
<term>
<termName>Term 2</termName>
<termId>587889</termId>
<termType>pt</termType>
</term>
<term>
<termName>Term 3</termName>
<termId></termId>
<termType>Nd</termType>
<relation>
<relationType>USE</relationType>
<termId></termId>
<termName>Term 4</termName>
</relation>
</term>
<term>
<termName>Term 4</termName>
<termId>587890</termId>
<termType>pt</termType>
</term>
</zthes>
produces the wanted, correct result:
<zthes>
<term>
<termName>Term 1</termName>
<termId>1</termId>
<termType>Nd</termType>
<relation>
<relationType>USE</relationType>
<termId>587889</termId>
<termName>Term 2</termName>
</relation>
</term>
<term>
<termName>Term 2</termName>
<termId>587889</termId>
<termType>pt</termType>
</term>
<term>
<termName>Term 3</termName>
<termId>2</termId>
<termType>Nd</termType>
<relation>
<relationType>USE</relationType>
<termId>587890</termId>
<termName>Term 4</termName>
</relation>
</term>
<term>
<termName>Term 4</termName>
<termId>587890</termId>
<termType>pt</termType>
</term>
</zthes>
Do note:
The identity rule is used to copy every node "as-is".
Templates override the identity rule only for elements that need to be modified.
<xsl:number>
is used for (me being lazy) calculating the required termId.A key is used for most efficient finding a
termId
by itsTermName
I'm reading between the lines a bit, but I think I know what you're trying to do.
I'd suggest doing this with two separate XSLs. It's certainly possible to combine two XSL passes into a single XSL, but the actual implementation differs depending on what XSL library you're using (there are some details here, if you are curious: http://www.oreillynet.com/xml/blog/2006/08/multistage_xslt_scripts.html )
The first pass should go through all of your terms, and assign numbers to the ones which are marked as 'Nd'. I'm guessing:
(a) That once they have an ID, they should have their type changed to "pt", and
(b) That the order of these elements is not specific.
The reason I suggest doing this as a separate pass is that one of your terms might reference another term which is itself an 'Nd' type. If this were the case, then it wouldn't have a valid number to reference (as none would have been assigned yet)
The XSL to perform this step would be something like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="term[termType = 'Nd']">
<term>
<xsl:copy-of select="termName"/>
<termId><xsl:value-of select="position()"/></termId>
<termType>pt</termType>
<xsl:copy-of select="relation" />
</term>
</xsl:template>
<xsl:template match="/">
<zthes>
<xsl:apply-templates select="zthes/term[termType = 'Nd']"/>
<xsl:copy-of select="zthes/term[termType = 'pt']"/>
</zthes>
</xsl:template>
</xsl:stylesheet>
The second step would be to take the output of that transform, and push it through a second transform which will 'fill in' the IDs of 'relation' terms. That's a fairly straightforward identity (exact, recursive copy) transform with one special rule in there. The special rule identifies termIds inside relation nodes, and substitutes in the correct termId based on name matching. This transform looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="relation/termId">
<termId>
<xsl:value-of select="/zthes/term[termName=current()/../termName]/termId"/>
</termId>
</xsl:template>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I expect that will do roughly what you're after. It won't work as-is on your current input data though, because your current input is actually invalid xml. :) To fix it:
- Change the 'Zthes' to 'zthes' in the opening tag. XML is case sensitive
- Inside the first 'relation' block, make sure you close the 'termId' node. At the moment you are opening it twice and not closing it.
- Inside the 'relation' block's 'termName', you've referenced 'Term2' when what you meant was 'Term 2'. There's a whitespace difference which will stop them from matching.
Good luck!
精彩评论