开发者

Split XML file into multiple files based on a threshold value

I need to split the following XML file based on a predetermined value, for this example, lets assume I want to limit the "Item" node to three (3) within each file created.

Here's a sample input XML file:

<Items>
  <Item>
    <Title>Title 1</Title>
    <DueDate>01-02-2008</DueDate>
  </Item>
  <Item>
    <Title>Title 2</Title>
    <DueDate>01-02-2009</DueDate>
  </Item>
  <Item>
    <Title>Title 3</Title>
    <DueDate>01-02-2010</DueDate>
  </Item>
  <Item>
    <Title>Title 4</Title>
    <DueDate>01-02-2011</DueDate>
  </Item>
  <Item>
    &开发者_如何学JAVAlt;Title>Title 5</Title>
    <DueDate>01-02-2012</DueDate>
  </Item>
  <Item>
    <Title>Title 6</Title>
    <DueDate>01-02-2013</DueDate>
  </Item>
  <Item>
    <Title>Title 7</Title>
    <DueDate>01-02-2013</DueDate>
  </Item>
</Items>

The desired output based on the threshold value of 3, would be three files, two of which contain 3 "Item", and the last one containing the remaining "items", which would be one.

Here's a sample of my XSLT which does allow me to split them for each item, which results into seven separate files, however, what I desire is to limit the size of the file based on a certain limit of "Item" node per file.

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">

<xsl:output method="xml" indent="yes" name="xml" />

<xsl:template match="/">

 <xsl:for-each select="//Item">
  <xsl:variable name="nTitle" select="Title"/>
  <xsl:variable name="filename" select="concat('Items\',$nTitle,'-','.xml')" />
  <xsl:value-of select="$filename" />
   <xsl:result-document  href="{$filename}"  format="xml">
         <xsl:copy-of select="."/>
  </xsl:result-document>
 </xsl:for-each>

</xsl:template>
</xsl:stylesheet>


This stylesheet:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:param name="pItemsNumber" select="3"/>
    <xsl:template match="Items">
        <xsl:for-each-group select="Item"
                            group-adjacent="(position()-1) idiv $pItemsNumber">
            <xsl:result-document  href="Items\{current-grouping-key()}.xml">
                <Items>
                    <xsl:copy-of select="current-group()"/>
                </Items>
            </xsl:result-document>
        </xsl:for-each-group>
    </xsl:template>
</xsl:stylesheet>

Output:

<?xml version="1.0" encoding="UTF-8"?>
<Items>
    <Item>
        <Title>Title 1</Title>
        <DueDate>01-02-2008</DueDate>
    </Item>
    <Item>
        <Title>Title 2</Title>
        <DueDate>01-02-2009</DueDate>
    </Item>
    <Item>
        <Title>Title 3</Title>
        <DueDate>01-02-2010</DueDate>
    </Item>
</Items>

<?xml version="1.0" encoding="UTF-8"?>
<Items>
    <Item>
        <Title>Title 4</Title>
        <DueDate>01-02-2011</DueDate>
    </Item>
    <Item>
        <Title>Title 5</Title>
        <DueDate>01-02-2012</DueDate>
    </Item>
    <Item>
        <Title>Title 6</Title>
        <DueDate>01-02-2013</DueDate>
    </Item>
</Items>

<?xml version="1.0" encoding="UTF-8"?>
<Items>
    <Item>
        <Title>Title 7</Title>
        <DueDate>01-02-2013</DueDate>
    </Item>
</Items>

Edit: Oops!


This transformation:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>
 <xsl:param name="pSplitNum" select="3"/>

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

 <xsl:template match="Item[position() mod $pSplitNum eq 1]">
  <xsl:result-document href=
   "file{position()}-{min((position()+$pSplitNum -1, count(/*/Item)))}.xml">
    <Items>
      <xsl:call-template name="identity"/>
      <xsl:apply-templates mode="copy" select=
      "following-sibling::Item[position() lt $pSplitNum]"/>
    </Items>
  </xsl:result-document>
 </xsl:template>
 <xsl:template match="/*"><xsl:apply-templates/></xsl:template>
 <xsl:template match="Item[position() mod $pSplitNum ne 1]"/>
</xsl:stylesheet>

when applied on the provided XML document:

<Items>
  <Item>
    <Title>Title 1</Title>
    <DueDate>01-02-2008</DueDate>
  </Item>
  <Item>
    <Title>Title 2</Title>
    <DueDate>01-02-2009</DueDate>
  </Item>
  <Item>
    <Title>Title 3</Title>
    <DueDate>01-02-2010</DueDate>
  </Item>
  <Item>
    <Title>Title 4</Title>
    <DueDate>01-02-2011</DueDate>
  </Item>
  <Item>
    <Title>Title 5</Title>
    <DueDate>01-02-2012</DueDate>
  </Item>
  <Item>
    <Title>Title 6</Title>
    <DueDate>01-02-2013</DueDate>
  </Item>
  <Item>
    <Title>Title 7</Title>
    <DueDate>01-02-2013</DueDate>
  </Item>
</Items>

produces the wanted three xml files:

Saxon 9.1.0.5J from Saxonica
Java version 1.6.0_22
Stylesheet compilation time: 645 milliseconds
Processing file:/C:/Program%20Files/Java/jre6/bin/marrowtr.xml
Building tree for file:/C:/Program%20Files/Java/jre6/bin/marrowtr.xml using class net.sf.saxon.tinytree.TinyBuilder
Tree built in 10 milliseconds
Tree size: 38 nodes, 119 characters, 0 attributes
Loading net.sf.saxon.event.MessageEmitter
Writing to file:/C:/Program%20Files/Java/jre6/bin/file1-3.xml
Writing to file:/C:/Program%20Files/Java/jre6/bin/file4-6.xml
Writing to file:/C:/Program%20Files/Java/jre6/bin/file7-7.xml
Execution time: 101 milliseconds
Memory used: 11453088
NamePool contents: 20 entries in 20 chains. 6 prefixes, 7 URIs

Do note:

  1. This is a simple application of the identity rule pattern.

  2. Every Item starting a new file is matched and it causes the wrapping in a top element, processing of itself and the next $pSplitNum -1 (or whatever remains in the last group), and outputting this as a single result-document (file).

  3. The name of every file created is: "filex-y.xml", where x and y are the starting and ending indexes of the Item elements written in the file.

  4. Every Item that isn't starting a new file is deleted by an empty matching template. Such elements are processed in "copy" mode.


You could implement a counter that is declared outside of your loop. When the counter hits 3, reset it and set a new filename. Otherwise, increment and append to the existing filename.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜