Comparing two lists and outputting differences in

2019-09-02 17:37发布

I have started this again, i was trying to get my head round the basics of this and have failed, below is the structure the XML will most likely take. and then what i need to output below that...

 <rtpm>
    <old>
        <simple>
            <information>
                <name1code>AAA</name1code>
                <name1use>N</name1use>
                <name2code>BBB</name2code>
                <name2use>P</name2use>
                <name3code>CCC</name3code>
                <name3use>N</name3use>
                <name4code>DDD</name4code>
                <name4use>N</name4use>
                <name5code>EEE</name5code>
                <name5use>N</name5use>
            </information>  
        </simple>
    </old>
    <new>
        <simple>
            <information>
                <name1code>AAA</name1code>
                <name1use>N</name1use>
                <name2code>BBC</name2code>
                <name2use>P</name2use>
                <name3code>AFD</name3code>
                <name3use>N</name3use>
                <name4code>CCC</name4code>
                <name4use>N</name4use>
                <name5code>EEE</name5code>
                <name5use>N</name5use>
            </information>
        </simple>
    </new>
</rtpm>

I would then need this to perform what was previously described and output as

<request>
    <query0>BBC</query0>
    <use0>P</use0>
    <query1>AFD</query1>
    <use1>N</use1>
</request>

The code currently in use is as per below but i have no idea how to make it just pick up name1code, 2code etc for the old key and the use to just pick up the use value when a 'not' match is found for the code.

  <xsl:key name="old" match="old/simple/information/*" use="." />
<xsl:key name="use" match="new/simple/information/*" use="name()" />

<xsl:template match="/*">
<request>
    <xsl:for-each select="new/simple/information/*[not(key('old', .))]">
        <xsl:element name="query{position() - 1}">
            <xsl:value-of select="." />
        </xsl:element>
        <xsl:element name="use{position() - 1}">
            <xsl:value-of select="key('use', concat(name(), 'use'))" /> <!--2-->        
        </xsl:element>
    </xsl:for-each>
</request>
</xsl:template>

<!--2--> is the line i can't get my head around.

Thank you too all that have helped so far, you have been a huge help. i say i have failed getting my head round it, but i get what/how this works to a degree, just not how to further it.

标签: xml arrays xslt
2条回答
劫难
2楼-- · 2019-09-02 18:14

Re your followup question: not sure what exactly "following by a tag" means. In any case, the answer is (again) to use a key to match "related" values, for example:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:key name="old" match="old/*" use="." />
<xsl:key name="use" match="detail/*" use="name()" />

<xsl:template match="/">
<output>
    <xsl:for-each select="rtpm/new/*[not(key('old', .))]">
        <xsl:element name="query{position() - 1}">
            <value><xsl:value-of select="." /></value>
            <use><xsl:value-of select="key('use', concat(name(), 'use'))" /></use>
        </xsl:element>
    </xsl:for-each>
</output>
</xsl:template>
</xsl:stylesheet> 
查看更多
Rolldiameter
3楼-- · 2019-09-02 18:21

One way is to use a key to look up the old values

<xsl:key name="old" match="old/*" use="text()" />

Then, to select new elements with values don't occur in the list of old elements, you would simply do this (assuming you were positioned on rtpm)

<xsl:apply-templates select="new/*[not(key('old', text()))]" />

And in the template that matches this, to create the relevant query element, you would make use of the position() function (as this is relative to the nodes you have just selected, not the position of them in the hierarchy)

 <xsl:element name="query{position() - 1}">
   <xsl:value-of select="text()" />
 </xsl:element>

Try this XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output omit-xml-declaration="yes" indent="yes" />
   <xsl:key name="old" match="old/*" use="text()" />
   <xsl:template match="/*">
     <xsl:apply-templates select="new/*[not(key('old', text()))]" />
   </xsl:template>

   <xsl:template match="new/*">
     <xsl:element name="query{position() - 1}">
       <xsl:value-of select="text()" />
     </xsl:element>
   </xsl:template>
</xsl:stylesheet>

When applied on your first XML sample, the following is output

<query0>ABB</query0>
<query1>ACD</query1>

In the second case, the following is also output

<query0>ABB</query0>
<query1>ACD</query1>

EDIT: The question has changed somewhat since it was originally asked (It should really have been asked as a new question), but to answer this new question, the expression in <!-- 2 --> you are looking for is this

<xsl:value-of select="key('use', concat(substring-before(name(), 'code'), 'use'))" />

This is because the current node has a name of "nameXcode", and it needs to be changed to "nameXuse" to look it up in the key. So, you get the text before "code" and then append "use" on the end instead.

However, if the use node you are getting is always going to be the following sibling to the code node, you could just do this

<xsl:value-of select="following-sibling::*[1]" />

This will work because following-sibling will get the following sibling in the hierarchy for the current node you are on, and not the next one in the nodes you have just selected.

查看更多
登录 后发表回答