Grouping of a well defined flat XML for Access import (XSLT 1.0)

江枫思渺然 提交于 2019-12-23 05:26:16

问题


I have an output from our local accounting system, which consists of an envelope tag and just a repeated sequence of the fields, but not grouped. I have no prior experience in XSL but have been trying to find a solution for this and have found a solution, which works for XSLT 2.0. Unfortunately, it seems that Access 2007 only accepts XSLT 1.0 transformations.

Can you please help me find a XSLT 1.0 solution that works?

These are two records in the original flat XML:

<ENVELOPE>
  <DBCFIXED>  <DBCDATE>1-Apr-2011</DBCDATE>
    <DBCPARTY></DBCPARTY>
  </DBCFIXED>
  <DBCVCHTYPE>Stock Journal</DBCVCHTYPE>
  <DBCVCHNO>1</DBCVCHNO>
  <DBCVCHREF></DBCVCHREF>
  <DBCSTNO></DBCSTNO>
  <DBCSERVICETAXNO></DBCSERVICETAXNO>
  <DBCPANNO></DBCPANNO>
  <DBCCSTNO></DBCCSTNO>
  <DBCNARR>Opening balance transfar</DBCNARR>
  <DBCQTY>0.000 Kg</DBCQTY>
  <DBCRATE></DBCRATE>
  <DBCAMOUNT></DBCAMOUNT>
  <DBCADDLCOST></DBCADDLCOST>
  <DBCGROSSAMT></DBCGROSSAMT>
  <DBCLEDAMT></DBCLEDAMT>
  <DBCFIXED>  <DBCDATE></DBCDATE>
    <DBCPARTY>ME KN YARN BL 1</DBCPARTY>
  </DBCFIXED>
  <DBCVCHTYPE></DBCVCHTYPE>
  <DBCVCHNO></DBCVCHNO>
  <DBCVCHREF></DBCVCHREF>
  <DBCSTNO></DBCSTNO>
  <DBCSERVICETAXNO></DBCSERVICETAXNO>
  <DBCPANNO></DBCPANNO>
  <DBCCSTNO></DBCCSTNO>
  <DBCNARR></DBCNARR>
  <DBCQTY>0.150 Kg</DBCQTY>
  <DBCRATE>566.00/Kg</DBCRATE>
  <DBCAMOUNT>-84.90</DBCAMOUNT>
  <DBCADDLCOST></DBCADDLCOST>
  <DBCGROSSAMT></DBCGROSSAMT>
  <DBCLEDAMT></DBCLEDAMT>
</ENVELOPE>

For the import into Access, I need the data to be grouped into records starting with DBCFIXED until the next DBCFIXED. Something like:

<InventoryDaybook>
  <record>
    <DBCFIXED>  
      <DBCDATE>1-Apr-2011</DBCDATE>
      <DBCPARTY/>
    </DBCFIXED>
    <DBCVCHTYPE>Stock Journal</DBCVCHTYPE>
    <...></...>
  </record>
  <record>
    <...></...>
  </record>
/<InventoryDaybook>

The XSLT 2.0 code that works is

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
  <InventoryDaybook>
    <xsl:for-each-group select="*" group-starting-with="DBCFIXED">
      <record>
        <xsl:copy-of select="current-group()"/>
      </record>
    </xsl:for-each-group>
  </InventoryDaybook>
</xsl:template>
</xsl:stylesheet>

It is amazing what can be done with XSL but my knowledge seems just too little.. Thanks in advance for your help and your understanding.


回答1:


This could be achieved in XSLT1.0 by means of a xsl:key, that matches non-DBCFIXED elements, and uses the first preceding DBCFIXED element as the key

<xsl:key 
    name="records" 
    match="ENVELOPE/*[not(self::DBCFIXED)]" 
    use="generate-id(preceding-sibling::DBCFIXED[1])" />

Then, when you match the individual DBCFIXED elements, you can easily look-up the associated elements that make up the record

<xsl:apply-templates select="key('records', generate-id())" />

Here is the full XLST

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>
   <xsl:key name="records" match="ENVELOPE/*[not(self::DBCFIXED)]" use="generate-id(preceding-sibling::DBCFIXED[1])" />
   <xsl:template match="/ENVELOPE">
      <InventoryDaybook>
         <xsl:apply-templates select="DBCFIXED" />
      </InventoryDaybook>
   </xsl:template>

   <xsl:template match="DBCFIXED">
      <record>
         <xsl:copy-of select="." />
         <xsl:apply-templates select="key('records', generate-id())" />
      </record>
   </xsl:template>

   <xsl:template match="@*|node()">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
   </xsl:template>
</xsl:stylesheet>

When applied to the input XML, the following is output

<InventoryDaybook>
   <record>
      <DBCFIXED>
         <DBCDATE>1-Apr-2011</DBCDATE>
         <DBCPARTY/>
      </DBCFIXED>
      <DBCVCHTYPE>Stock Journal</DBCVCHTYPE>
      <DBCVCHNO>1</DBCVCHNO>
      <DBCVCHREF/>
      <DBCSTNO/>
      <DBCSERVICETAXNO/>
      <DBCPANNO/>
      <DBCCSTNO/>
      <DBCNARR>Opening balance transfar</DBCNARR>
      <DBCQTY>0.000 Kg</DBCQTY>
      <DBCRATE/>
      <DBCAMOUNT/>
      <DBCADDLCOST/>
      <DBCGROSSAMT/>
      <DBCLEDAMT/>
   </record>
   <record>
      <DBCFIXED>
         <DBCDATE/>
         <DBCPARTY>ME KN YARN BL 1</DBCPARTY>
      </DBCFIXED>
      <DBCVCHTYPE/>
      <DBCVCHNO/>
      <DBCVCHREF/>
      <DBCSTNO/>
      <DBCSERVICETAXNO/>
      <DBCPANNO/>
      <DBCCSTNO/>
      <DBCNARR/>
      <DBCQTY>0.150 Kg</DBCQTY>
      <DBCRATE>566.00/Kg</DBCRATE>
      <DBCAMOUNT>-84.90</DBCAMOUNT>
      <DBCADDLCOST/>
      <DBCGROSSAMT/>
      <DBCLEDAMT/>
   </record>
</InventoryDaybook>



回答2:


Congratulations to Tim for first correct solution. Whilst Tim's is absolutely correct, I just want to bring to the OP's attention, that there are two general forms of solution to XSLT 1.0 group-starting-with - One where the head node is excluded from the key (as in Tim's case), and the other where the head node is included (shown below). I am not sure which is superior. Perhaps Dimitre can tell us.

So for interest sake, here is the alternate form, which includes the head node in the key.

Head-node-included-in-key form

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>
   <xsl:key name="records" match="ENVELOPE/*" use="generate-id(
            (preceding-sibling::DBCFIXED|self::DBCFIXED)[last()])" />

   <xsl:template match="/ENVELOPE">
      <InventoryDaybook>
         <xsl:apply-templates select="DBCFIXED" mode="group-head" />
      </InventoryDaybook>
   </xsl:template>

   <xsl:template match="DBCFIXED" mode="group-head">
      <record>
         <xsl:apply-templates select="key('records', generate-id())" />
      </record>
   </xsl:template>

   <xsl:template match="@*|node()">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
   </xsl:template>
</xsl:stylesheet>

Very slight advantages

  1. key match condition simpler.
  2. One less line in the template for the head node.
  3. The head node goes through apply-templates rather than directly copy-of. This may be of benefit if you are doing more than just straight copy.

Very slight disadvantages

  1. Key use attribute more complicated
  2. Have to add modes to discriminate between grouping the head node and down-stream processing of it. Although in way, this is also an advantage, as the mode designators improve self-documentation.


来源:https://stackoverflow.com/questions/11625288/grouping-of-a-well-defined-flat-xml-for-access-import-xslt-1-0

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