0

I wish to filter records during an xsl transformation. I know basic filtering is quite simple, however, I wish it to be conditional based on data from the original source xml data.

My source data will have several updates for the same record in it. Each update will be identified with an event sequence number. I only want the last event sequence in my transformed data.

Here is a sample of my source data:

<?xml version="1.0" encoding="UTF-8"?>
<Msg xmlns="http://schemas.namespace.org/myschema/myschame.xsd" schemalocation="http://schemas.namespace.org/myschema/myschema.xsd ../myschema.xsd" schemaversion="5">
	<NumberofRecordsEnclosed>7</NumberofRecordsEnclosed>
	<Record>
		<EventSequenceNumber>3</EventSequenceNumber>
		<ReplaceRecord>
			<Id>
				<Number>1234</Number>
			</Id>
		</ReplaceRecord>
	</Record>
	<Record>
		<EventSequenceNumber>4</EventSequenceNumber>
		<ReplaceRecord>
			<Id>
				<Number>1234</Number>
			</Id>
		</ReplaceRecord>
	</Record>
	<Record>
		<EventSequenceNumber>5</EventSequenceNumber>
		<ReplaceRecord>
			<Id>
				<Number>1234</Number>
			</Id>
		</ReplaceRecord>
	</Record>
	<Record>
		<EventSequenceNumber>6</EventSequenceNumber>
		<ReplaceRecord>
			<Id>
				<Number>1234</Number>
			</Id>
		</ReplaceRecord>
	</Record>
	<Record>
		<EventSequenceNumber>1</EventSequenceNumber>
		<ReplaceRecord>
			<Id>
				<Number>5678</Number>
			</Id>
		</ReplaceRecord>
	</Record>
	<Record>
		<EventSequenceNumber>2</EventSequenceNumber>
		<ReplaceRecord>
			<Id>
				<Number>5678</Number>
			</Id>
		</ReplaceRecord>
	</Record>
	<Record>
		<EventSequenceNumber>7</EventSequenceNumber>
		<ReplaceRecord>
			<Id>
				<Number>1234</Number>
			</Id>
		</ReplaceRecord>
	</Record>
</Msg>

When I transform the document, I only want the following records:

<?xml version="1.0" encoding="UTF-8"?>
<Msg xmlns="http://schemas.namespace.org/myschema/myschame.xsd" schemalocation="http://schemas.namespace.org/myschema/myschema.xsd ../myschema.xsd" schemaversion="5">
	<NumberofRecordsEnclosed>7</NumberofRecordsEnclosed>
	<Record>
		<EventSequenceNumber>2</EventSequenceNumber>
		<ReplaceRecord>
			<Id>
				<Number>5678</Number>
			</Id>
		</ReplaceRecord>
	</Record>
	<Record>
		<EventSequenceNumber>7</EventSequenceNumber>
		<ReplaceRecord>
			<Id>
				<Number>1234</Number>
			</Id>
		</ReplaceRecord>
	</Record>
</Msg>

As you can see, there are only two records that I care about: Where 'EventSequenceNumber' is 7 for 'Number' 1234 Where 'EventSequenceNumber' is 2 for 'Number' 5678 The 'EventSequenceNumber' data is a true sequence number. If I receive another XML source data that contains 'Number' 1234, the 'EventSequenceNumber' will continue from 8 onwards.

Is this possible with XSL?
Could somenody please assist in logic to go from the above source & result?

Thankyou.

1
Contributor
1
Reply
2
Views
8 Years
Discussion Span
Last Post by rat1973
0

I have finally found a solution to my problem. I have found it disappointing to get no feedback though. Perhaps this is not the best forum to get assistance....

That aside - I do wish to post my solution for anyone that may be interested. Two style sheets were required, one to perform a sort, and another to perform the required filter. I then used a program to transform the document through one XSL, I then used the resulting output of this transformation as input to a second transformation.

XSL documents:

one.xsl:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:pre="http://schemas.namespace.org/myschema/myschame.xsd">
   <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

   <xsl:template match="/">
         <xsl:apply-templates select="pre:Msg" />
   </xsl:template>

   <xsl:template match="pre:Msg">
      <xsl:copy>

         
         <xsl:for-each select="pre:Record">

            <xsl:sort select="pre:ReplaceRecord/pre:Id/pre:Number" />
            <xsl:sort select="pre:EventSequenceNumber" data-type="number"/>
            <xsl:copy-of select="."/>
         </xsl:for-each>

      </xsl:copy>

   </xsl:template>

</xsl:stylesheet>

The above xsl is a rather simple sort xsl document.

two.xsl

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:pre="http://schemas.namespace.org/myschema/myschame.xsd">
   <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
   <xsl:key name="VehList" match="pre:Msg/pre:Record/pre:ReplaceRecord" use="pre:Id/pre:Number" />

   <xsl:template match="/">
      <xsl:element name="Root">

         <xsl:apply-templates select="pre:Msg" />

      </xsl:element>
   </xsl:template>


   <xsl:template match="pre:Msg">

      <xsl:for-each select="pre:Record/pre:ReplaceRecord">
         <xsl:variable name="EventSeq" select="preceding-sibling::pre:EventSequenceNumber" />
            <xsl:variable name="Rec_Cnt" select="count(key('VehList',pre:Id/pre:Number))" />
            <xsl:if test="position()=$Rec_Cnt or position()=$Rec_Cnt+last()-$Rec_Cnt">
               <xsl:element name="Record">
                  <xsl:element name="StockNumber">
                     <xsl:value-of select="pre:Id/pre:Number"/>
                  </xsl:element>
                  <xsl:element name="Sequence">
                     <xsl:value-of select="$EventSeq"/>
                  </xsl:element>
               </xsl:element>
            </xsl:if>
      </xsl:for-each>

   </xsl:template>


</xsl:stylesheet>

The above xsl will get the last occurance of each unique 'Number' tag from my source document.

A snippet of my java code is:

String inXML = "C:/tmp/source.xml";
        String inXSL = "C:/tmp/one.xsl";
        String outTXT = "C:/tmp/output.xml";


    	TransformerFactory factory = TransformerFactory.newInstance();
	StreamSource xslStream = new StreamSource(inXSL);
    	Transformer transformer = factory.newTransformer(xslStream);
    	transformer.setErrorListener(new MyErrorListener());

       	ByteArrayOutputStream myOut = new ByteArrayOutputStream();
       	ByteArrayInputStream myIn;

       	StreamSource in = new StreamSource(inXML);
       	StreamResult out = new StreamResult(myOut);
        	
       	transformer.transform(in,out);

        inXSL = "C:/tmp/two.xsl";

       	xslStream = new StreamSource(inXSL);
       	myIn = new ByteArrayInputStream(myOut.toByteArray());
       	in = new StreamSource(myIn);
       	out = new StreamResult(outTXT);
       	transformer = factory.newTransformer(xslStream);
       	transformer.setErrorListener(new MyErrorListener());
       	transformer.transform(in,out);

The code is not optimised by any means, it is just a snippet of a test program to test my theories. The output to the above process is:

<?xml version="1.0" encoding="UTF-8"?>
<Root>
   <Record>
      <StockNumber>1234</StockNumber>
      <Sequence>7</Sequence>
   </Record>
   <Record>
      <StockNumber>5678</StockNumber>
      <Sequence>2</Sequence>
   </Record>
</Root>

I tried to work with imports, but it didn't give me the desired result. I beleive there is a more effecient method using pure xsl (rather than two passes in code), but am yet to discover it. If anyone knows how to get the same result with using xsl:imports (or something similar), then I would really appreciate some input (based on responses to my previous posts, I am not holding my breath).

This topic has been dead for over six months. Start a new discussion instead.
Have something to contribute to this discussion? Please be thoughtful, detailed and courteous, and be sure to adhere to our posting rules.