Petras
Petras

Reputation: 591

how to get some documents from one xml

I would like to get distinct documents from my xml on multiple levels.

My xml file:

<?xml version="1.0" encoding="UTF-8"?>
<document>
    <line id="0">
        <Info><![CDATA[Header]]></Info>
        <documentNUM><![CDATA[DOC1]]></documentNUM>
        <Code><![CDATA[AS22]]></Code>
    </line>
    <line id="1">
        <Info><![CDATA[Line]]></Info>
        <Position><![CDATA[1]]></Position>
        <Number><![CDATA[7361]]></Number>
    </line>
    <line id="2">
        <Info><![CDATA[Line]]></Info>
        <Position><![CDATA[2]]></Position>
        <Number><![CDATA[7362]]></Number>
    </line>     
    <line id="3">
        <Info><![CDATA[Header]]></Info>
        <documentNUM><![CDATA[DOC2]]></documentNUM>
        <Code><![CDATA[AS22]]></Code>
    </line>
    <line id="4">
        <Info><![CDATA[Line]]></Info>
        <Position><![CDATA[1]]></Position>
        <Number><![CDATA[3623]]></Number>
    </line> 
    <line id="5">
        <Info><![CDATA[Header]]></Info>
        <documentNUM><![CDATA[DOC1]]></documentNUM>
        <Code><![CDATA[AS22]]></Code>
    </line>
    <line id="6">
        <Info><![CDATA[Line]]></Info>
        <Position><![CDATA[1]]></Position>
        <Number><![CDATA[3623]]></Number>
    </line> 
</document>

From this xml I should get two documents, for it I use key function:

<xsl:key name="kNext" match="line[starts-with(Info,'H')]" use="concat(starts-with(Info,'H'), '+', documentNUM)"/>

And for both documents 3 and 1 lines.

Needed result:

<result>
    <Group>
        <Message>
            <document>
                <documentNUM>DOC1</documentNUM> 
                <Lines>
                    <Line>
                        <LineNumber>1</LineNumber>
                        <Number>7361</Number>
                        <Code>AS22</Code>
                    </Line>
                    <Line>
                        <LineNumber>2</LineNumber>
                        <Number>7362</Number>
                        <Code>AS22</Code>
                    </Line>
                    <Line>
                        <LineNumber>3</LineNumber>
                        <Number>3623</Number>
                        <Code>AS22</Code>
                    </Line>
                </Lines>
            </document>
        </Message>
    </Group>
    <Group>
        <Message>
            <document>
                <documentNUM>DOC2</documentNUM>
            </document> 
                    <Line>
                        <LineNumber>1</LineNumber>
                        <Number>3623</Number>
                        <Code>AS22</Code>
                    </Line>
                </Lines>
        </Message>
    </Group>
</result>

I'm stuck to finding lines. For both documents calculate the same lines. Please give me some advice how to get correct answer.

P.S. Please correct my question if it needed.

Upvotes: 1

Views: 67

Answers (2)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243479

Here is a complete XSLT 1.0 solution:

<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="kFollowing" match="line[not(documentNUM)]"
  use="generate-id(preceding-sibling::line
                               [documentNUM]
                                          [1]
                  )"/>

 <xsl:key name="kLineByDocNum" match="line"
  use="documentNUM"/>

 <xsl:template match=
  "line
     [documentNUM
    and
      generate-id()
     =
      generate-id(key('kLineByDocNum', documentNUM)[1])
     ]">
   <Group>
    <Message>
      <document>
        <documentNUM>
          <xsl:value-of select="documentNUM"/>
        </documentNUM>
        <Lines>
          <xsl:apply-templates mode="inGroup" select=
          "key('kLineByDocNum', documentNUM)"/>
        </Lines>
      </document>
    </Message>
   </Group>
 </xsl:template>

 <xsl:template match="line" mode="inGroup">
  <xsl:apply-templates mode="inGroup2"
  select="key('kFollowing', generate-id())">
  <xsl:with-param name="pCode" select="Code"/>
  </xsl:apply-templates>
 </xsl:template>

 <xsl:template match="line" mode="inGroup2">
  <xsl:param name="pCode"/>

  <xsl:variable name="vcurDocNum" select=
  "preceding-sibling::line
            [documentNUM][1]
                  /documentNUM
  "/>
  <xsl:variable name="vPos" select=
  "count(preceding-sibling::line
                [not(documentNUM)]
                  [preceding-sibling::line
                     [documentNUM][1]
                         /documentNUM
                  =
                   $vcurDocNum
                  ]
        ) +1"/>
  <Line>
    <LineNumber><xsl:value-of select="$vPos"/></LineNumber>
    <xsl:copy-of select="Number"/>
    <Code><xsl:value-of select="$pCode"/></Code>
  </Line>
 </xsl:template>
 <xsl:template match="text()"/>
</xsl:stylesheet>

when this transformation is applied on the provided XML document:

<document>
    <line id="0">
        <Info>Header</Info>
        <documentNUM>DOC1</documentNUM>
        <Code>AS22</Code>
    </line>
    <line id="1">
        <Info>Line</Info>
        <Position>1</Position>
        <Number>7361</Number>
    </line>
    <line id="2">
        <Info>Line</Info>
        <Position>2</Position>
        <Number>7362</Number>
    </line>
    <line id="3">
        <Info>Header</Info>
        <documentNUM>DOC2</documentNUM>
        <Code>AS22</Code>
    </line>
    <line id="4">
        <Info>Line</Info>
        <Position>1</Position>
        <Number>3623</Number>
    </line>
    <line id="5">
        <Info>Header</Info>
        <documentNUM>DOC1</documentNUM>
        <Code>AS22</Code>
    </line>
    <line id="6">
        <Info>Line</Info>
        <Position>1</Position>
        <Number>3623</Number>
    </line>
</document>

the wanted, correct result is produced:

<Group>
   <Message>
      <document>
         <documentNUM>DOC1</documentNUM>
         <Lines>
            <Line>
               <LineNumber>1</LineNumber>
               <Number>7361</Number>
               <Code>AS22</Code>
            </Line>
            <Line>
               <LineNumber>2</LineNumber>
               <Number>7362</Number>
               <Code>AS22</Code>
            </Line>
            <Line>
               <LineNumber>3</LineNumber>
               <Number>3623</Number>
               <Code>AS22</Code>
            </Line>
         </Lines>
      </document>
   </Message>
</Group>
<Group>
   <Message>
      <document>
         <documentNUM>DOC2</documentNUM>
         <Lines>
            <Line>
               <LineNumber>1</LineNumber>
               <Number>3623</Number>
               <Code>AS22</Code>
            </Line>
         </Lines>
      </document>
   </Message>
</Group>

Upvotes: 2

LarsH
LarsH

Reputation: 27994

This looks like a grouping problem. If you have XSLT 2.0 available, use for-each-group / group-starting-with. Then you don't need a key:

<result>
  <xsl:for-each-group select="line"
       group-starting-with="line[starts-with(Info,'H')]">
    <Group>
      <Message>
        <document>
          <documentNUM>
            <xsl:value-of select="current-grouping-key()/documentNUM" />
          </documentNUM>
        </document>
        <xsl:apply-templates select="current-group()
                          [not(starts-with(Info, 'H'))]" />

etc.

If you want more detail, let me know.

Upvotes: 0

Related Questions