tags:

views:

424

answers:

5

Hello to all. About week age I asked the question here and got a great help. Now my question has continuation. Originally I had to convert XML into text file. Here's a sample XML file:

<DOC xsi:noNamespaceSchemaLocation="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
<DOC_REQUISITES DOC_DATE="2009-04-23" DOC_NO="99999999"/>
<DOCID TradeDate="2009-04-23" Weekday="Monday" MainFirmId="ZXC0000" FirmName="Firm Name" FirmINN="1234567899">
    <FIRM FirmID="FirmId">
        <CURRENCY CurrencyId="USD">
        <DEPARTMENT DepartmentId="ABCD" DepName="Department Name1">
            <SETTLEDATE SettleDate="2009-04-23">
                <SECURITY SecurityId="QAZ" SecShortName="SecName1" SecurityType="dc" FaceValue="5">
                    <TRDACC TrdAccId="ABC00000">
                        <RECORDS RecNo="1" TradeNo="111" TradeTime="15:15:16" Price="10" Quantity="50" Value="500"/>
                    </TRDACC>
                    <TRDACC TrdAccId="SDC00000">
                        <RECORDS RecNo="2" TradeNo="112" TradeTime="15:15:16" Price="10" Quantity="50" Value="500"/>
                        <RECORDS RecNo="3" TradeNo="113" TradeTime="15:15:16" Price="20" Quantity="10" Value="200"/>
                    </TRDACC>
                </SECURITY>
                <SECURITY SecurityId="WSX" SecShortName="SecName2" SecurityType="dc" FaceValue="1">
                    <TRDACC TrdAccId="ABC00000">
                        <RECORDS RecNo="4" TradeNo="114" TradeTime="15:15:13" Price="2" Quantity="1" Value="2"/>
                    </TRDACC>
                </SECURITY>
            </SETTLEDATE>
        </DEPARTMENT>
        <DEPARTMENT DepartmentId="CBSD" DepName="Department Name2">
            <SETTLEDATE SettleDate="2009-05-20">
                <SECURITY SecurityId="RFV" SecShortName="SecName3" SecurityType="dc" FaceValue="2">
                    <TRDACC TrdAccId="SDC00000">
                        <RECORDS RecNo="5" TradeNo="115" TradeTime="15:15:13" Price="100" Quantity="10" Value="1000"/>
                    </TRDACC>
                </SECURITY>
            </SETTLEDATE>
        </DEPARTMENT>
    </CURRENCY>
  </FIRM>
</DOCID>

My desired output looks like this:

2009-04-23,99999999,2009-04-23,Monday,ZXC0000,Firm Name,1234567899,FirmId,USD,ABCD,Department Name1,2009-04-23,QAZ,SecName1,dc,5,ABC00000,1,111,15:15:16,10,50,500
2009-04-23,99999999,2009-04-23,Monday,ZXC0000,Firm Name,1234567899,FirmId,USD,ABCD,Department Name1,2009-04-23,QAZ,SecName1,dc,5,SDC00000,2,112,15:15:16,10,50,500
2009-04-23,99999999,2009-04-23,Monday,ZXC0000,Firm Name,1234567899,FirmId,USD,ABCD,Department Name1,2009-04-23,QAZ,SecName1,dc,5,SDC00000,3,113,15:15:16,20,10,200
2009-04-23,99999999,2009-04-23,Monday,ZXC0000,Firm Name,1234567899,FirmId,USD,ABCD,Department Name1,2009-04-23,WSX,SecName2,dc,1,ABC00000,4,114,15:15:13,2,1,2
2009-04-23,99999999,2009-04-23,Monday,ZXC0000,Firm Name,1234567899,FirmId,USD,CBSD,Department Name2,2009-05-20,RFV,SecName3,dc,2,SDC00000,5,115,15:15:13,100,10,1000

This XSLT was posted for me and it works just great. I really appreciate it.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:output method="text" indent="yes" encoding="UTF-8" omit-xml-declaration="yes"/>

    <xsl:template match="/">
       <xsl:apply-templates select="/DOC/DOCID/FIRM/CURRENCY/DEPARTMENT/SETTLEDATE/SECURITY/TRDACC"/>
    </xsl:template>

<xsl:template match="TRDACC">
    <!--Select all of the attribute values from the preceding DOC_REQUISITES attibutes, TRDACC ancestor elements, the current element, and all of it's descendants and then apply-templates to those attributes -->
    <xsl:apply-templates select="preceding::DOC_REQUISITES/@* | ancestor::*[not(local-name()='DOC')]/@* | @* | descendant::*/@*"/>
    <!--Adds a carriage return at the end of the line -->
    <xsl:value-of select="'&#10;'"/>
</xsl:template>

<!--Template match for attributes that emits the attribute value and a ',', except for the last one -->
<xsl:template match="@*[.!='']">
    <xsl:value-of select="."/>
    <xsl:if test="position()!=last()">,</xsl:if>
</xsl:template>

</xsl:stylesheet>

Here is my question: The produced output file later will be load into Data Base. The table's columns correspond to the xml file attributes. Some columns allowed Null. It means some attributes could be omitted. For example if attr. DOC_NO, Weekday, SettleDate, RecNo not present the output record must be looks like this:

2009-04-23,,2009-04-23,,ZXC0000,Firm Name,1234567899,FirmId,USD,ABCD,Department Name1,,QAZ,SecName1,dc,5,ABC00000,,111,15:15:16,10,50,500

In other words I need to check for existence of each attribute. I try to solve it but my xslt knowledge very limited. Please help me. Thanks.

A: 

Without a detailed analysis, I think all you have to do is change the last template and remove the not-empty test [.!=''] so it looks like this

<xsl:template match="@*">
Jim Garrison
Jim, thanks for replying. I think I have to do something like: <xsl:choose> <xsl:when test="@Weekday"> <xsl:value-of select="@Weekday"/> <xsl:text>,</xsl:text> </xsl:when> <xsl:otherwise> <xsl:text>,</xsl:text> </xsl:otherwise></xsl:choose>for each possible attribute. So if it exists it will put value in the record, if not just ",". But I don't know how to embed this into XSLT I already have.
klipa
What you suggesting working if any attribute ="", but I need to check for attribute existence, because there is a list of possible attributes which is match to the table columns and XML input file simply may not have all of them.
klipa
A: 

Jim, thanks for replying. (I'm not sure where to write the answer).

I think I have to do something like that for each possible attribute:

<xsl:choose>
    <xsl:when test="@Weekday">
        <xsl:value-of select="@Weekday"/>
        <xsl:text>,</xsl:text>
    </xsl:when>
    <xsl:otherwise>
        <xsl:text>,</xsl:text>
    </xsl:otherwise>
</xsl:choose>

So if it exists it will put value in the record, if not just ",". But I don't know how to embed this into XSLT I already have.

What you suggesting working if any attribute =" " , but I need to check for attribute existence, because there is a list of possible attributes which is match to the table columns and XML input file simply may not have all of them. – klipa 3 hours ago [delete this comment]

klipa
A: 

Since the output will be loaded into a database, the ordering of the columns in output is important.

Since the string value of an empty nodeset is the empty string, no test is needed for existence of any attribute.

I produced the output differently in DOCID and SECURITY, use the style that seems cleaner to you.

I have also added xsl:strip-space to ignore the whitespace in the input document.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;

    <xsl:output method="text" />
    <xsl:strip-space elements="*" />

    <xsl:template match="RECORDS">
      <xsl:apply-templates select="ancestor::DOCID" mode="print" />
      <xsl:apply-templates select="ancestor::FIRM" mode="print" />
      <xsl:apply-templates select="ancestor::FIRM/CURRENCY" mode="print" />
      <xsl:apply-templates select="ancestor::DEPARTMENT" mode="print" />
      <xsl:apply-templates select="ancestor::SETTLEDATE" mode="print" />
      <xsl:apply-templates select="ancestor::SECURITY" mode="print" />
      <xsl:apply-templates select="ancestor::TRDACC" mode="print" />

      <xsl:value-of select="@RecNo" />
      <xsl:text>,</xsl:text>
      <xsl:value-of select="@TradeNo" />
      <xsl:text>,</xsl:text>
      <xsl:value-of select="@TradeTime" />
      <xsl:text>,</xsl:text>
      <xsl:value-of select="@Price" />
      <xsl:text>,</xsl:text>
      <xsl:value-of select="@Quantity" />
      <xsl:text>,</xsl:text>
      <xsl:value-of select="@Value" />
      <xsl:text>&#10;&#13;</xsl:text>

    </xsl:template>

    <xsl:template match="DOCID" mode="print">
      <xsl:value-of select="@TradeDate" />
      <xsl:text>,</xsl:text>
      <xsl:value-of select="@Weekday" />
      <xsl:text>,</xsl:text>
      <xsl:value-of select="@WMainFirmId" />
      <xsl:text>,</xsl:text>
      <xsl:value-of select="@FirmName" />
      <xsl:text>,</xsl:text>
      <xsl:value-of select="@WFirmINN" />
      <xsl:text>,</xsl:text>
    </xsl:template>

    <xsl:template match="SECURITY" mode="print">
      <xsl:value-of select="concat(@SecurityId,',',@SecShortName,',',@SecurityType,',',@FaceValue,',')" />
    </xsl:template>

    <!-- etc for each element we apply in the RECORD template -->

</xsl:stylesheet>
Lachlan Roche
Thanks for answer. I tried to run script you offered, but it doesn't produced any output?
klipa
@klipa Sorry, earlier I wrote RECORD instead of RECORDS.
Lachlan Roche
A: 

Lachlan thanks a lot. I combine both XSL files and got exactly what I need. If you interested here is my final XSL solution:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
<xsl:output method="text" indent="yes" encoding="UTF-8" omit-xml-declaration="yes"/>

<xsl:template match="/">
    <xsl:apply-templates select="/DOC/DOCID/FIRM/CURRENCY/DEPARTMENT/SETTLEDATE/SECURITY/TRDACC/RECORDS"/>
</xsl:template>

<xsl:template match="RECORDS">
    <xsl:apply-templates select="preceding::DOC_REQUISITES" mode="print" />
    <xsl:apply-templates select="ancestor::DOCID" mode="print" />
    <xsl:apply-templates select="ancestor::FIRM" mode="print" />
    <xsl:apply-templates select="ancestor::FIRM/CURRENCY" mode="print" />
    <xsl:apply-templates select="ancestor::DEPARTMENT" mode="print" />
    <xsl:apply-templates select="ancestor::SETTLEDATE" mode="print" />
    <xsl:apply-templates select="ancestor::SECURITY" mode="print" />
    <xsl:apply-templates select="ancestor::TRDACC" mode="print" />

    <xsl:value-of select="@RecNo" />
    <xsl:text>,</xsl:text>
    <xsl:value-of select="@TradeNo" />
    <xsl:text>,</xsl:text>
    <xsl:value-of select="@TradeTime" />
    <xsl:text>,</xsl:text>
    <xsl:value-of select="@Price" />
    <xsl:text>,</xsl:text>
    <xsl:value-of select="@Quantity" />
    <xsl:text>,</xsl:text>
    <xsl:value-of select="@Value" />
    <xsl:text>&#10;</xsl:text>
</xsl:template>

<xsl:template match="DOC_REQUISITES" mode="print">
    <xsl:value-of select="@DOC_DATE" />
    <xsl:text>,</xsl:text>
    <xsl:value-of select="@DOC_NO" />
    <xsl:text>,</xsl:text>
</xsl:template>

<xsl:template match="DOCID" mode="print">
    <xsl:value-of select="@TradeDate" />
    <xsl:text>,</xsl:text>
    <xsl:value-of select="@Weekday" />
    <xsl:text>,</xsl:text>
    <xsl:value-of select="@MainFirmId" />
    <xsl:text>,</xsl:text>
    <xsl:value-of select="@FirmName" />
    <xsl:text>,</xsl:text>
    <xsl:value-of select="@FirmINN" />
    <xsl:text>,</xsl:text>
</xsl:template>

<xsl:template match="FIRM" mode="print">
    <xsl:value-of select="@FirmID" />
    <xsl:text>,</xsl:text>
</xsl:template>

<xsl:template match="CURRENCY" mode="print">
    <xsl:value-of select="@CurrencyId" />
    <xsl:text>,</xsl:text>
</xsl:template>

<xsl:template match="DEPARTMENT" mode="print">
    <xsl:value-of select="@DepartmentId" />
    <xsl:text>,</xsl:text>
    <xsl:value-of select="@DepName" />
    <xsl:text>,</xsl:text>
</xsl:template>

<xsl:template match="SETTLEDATE" mode="print">
        <xsl:value-of select="@SettleDate" />
        <xsl:text>,</xsl:text>
</xsl:template>

<xsl:template match="SECURITY" mode="print">
    <xsl:value-of select="concat(@SecurityId,',',@SecShortName,',',@SecurityType,',',@FaceValue,',')" />
</xsl:template>

<xsl:template match="TRDACC" mode="print">
            <xsl:value-of select="@TrdAccId" />
            <xsl:text>,</xsl:text>
</xsl:template>

Thanks again to everybody who help me !!!

klipa
@klipa If you wish to leave a comment to thank someone, please add a comment to their answer, and the system will notify them. If a particular answer was the most helpful, it can be accepted by clicking the checkbox outline to the left of the answer.
Lachlan Roche
+1  A: 

The previous version of the stylesheet relied on the structure of the XML document to drive a lot of the output, often referred to as the 'push' method.

If you aren't sure that you will have some of the attributes, then you can use a stylesheet that is more of a 'pull' and specifically ask for certain attributes in a specific order:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:output method="text" indent="yes" encoding="UTF-8" omit-xml-declaration="yes"/>

    <xsl:template match="/">
       <xsl:apply-templates select="/DOC/DOCID/FIRM/CURRENCY/DEPARTMENT/SETTLEDATE/SECURITY/TRDACC"/>
    </xsl:template>

<xsl:template match="TRDACC">
    <xsl:value-of select="preceding::DOC_REQUISITES/@DOC_DATE"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="preceding::DOC_REQUISITES/@DOC_NO"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="ancestor::DOCID/@TradeDate"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="ancestor::DOCID/@Weekday"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="ancestor::DOCID/@MainFirmId"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="ancestor::DOCID/@FirmName"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="ancestor::DOCID/@FirmINN"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="ancestor::FIRM/@FirmID"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="ancestor::CURRENCY/@CurrencyId"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="ancestor::DEPARTMENT/@DepartmentId"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="ancestor::DEPARTMENT/@DepName"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="ancestor::SETTLEDATE/@SettleDate"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="ancestor::SECURITY/@SecurityId"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="ancestor::SECURITY/@SecShortName"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="ancestor::SECURITY/@SecurityType"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="ancestor::SECURITY/@FaceValue"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="@TrdAccId"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="RECORDS/@RecNo"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="RECORDS/@TradeNo"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="RECORDS/@TradeTime"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="RECORDS/@Price"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="RECORDS/@Quantity"/>
    <xsl:text>,</xsl:text>
    <xsl:value-of select="RECORDS/@Value"/>
    <xsl:text>&#10;</xsl:text>

</xsl:template>

</xsl:stylesheet>

If the attribute does not exist, then is selects nothing for the value and leaves the empty spot between the commas.

Mads Hansen
Thanks Mads. Just so you answer. I'm not in the office and will try later your script. (Don't have testing environment at home).I'll notify you how it works for me.Thanks again.
klipa