Wrox Programmer Forums
Go Back   Wrox Programmer Forums > XML > XSLT
|
XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
 
Old September 27th, 2007, 07:54 PM
Registered User
 
Join Date: Sep 2007
Posts: 2
Thanks: 0
Thanked 0 Times in 0 Posts
Send a message via AIM to Vartan
Default Help needed transforming plist to XML by XSL

The Apple plist file format is a very special kind of xml, so I plan to convert it to a more standard XML file.

A example plist file is
Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
    <dict>
        <key>NAME</key>
        <integer>AName</integer>
        <key>CATEGORIES</key>
        <array>
            <dict>
                <key>CATEGORY_ID</key>
                <integer>1</integer>
                <key>ITEM_TYPE_ID</key>
                <integer>102</integer>
                <key>NAME</key>
                <string>CategoryName</string>
                <key>USER_ADDED</key>
                <false/>
            </dict>
        </array>
        <key>THE_ID</key>
        <integer>0</integer>
        <key>THE_PLACE</key>
        <string>APlaceName</string>
    </dict>
</array>
</plist>
In this example the plist file is only two level but it could be 3 or even more.

As you can see I need to convert pairs of nodes to node names and value.

I'm new to XSLT and I tried this XSLT
Code:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

        <xsl:output method="xml" indent="yes"/>
        <xsl:strip-space elements="*"/>

        <xsl:template match="/plist">
            <xsl:apply-templates select="array"/>
        </xsl:template>

        <xsl:template match="array">
            <xsl:for-each select="dict">
                <xsl:apply-templates select="key"/>
            </xsl:for-each>
        </xsl:template>

        <xsl:template match="key">
            <xsl:element name="{translate(text(), ' ', '_')}">
                <xsl:for-each select="../array/dict">
                    <xsl:apply-templates select="key"/>
                </xsl:for-each>

            <xsl:value-of select="following-sibling::node()[1]"/>
            </xsl:element>
        </xsl:template>
     </xsl:stylesheet>
If you try that you'll notice a few nesting problems. Another mistake is that the second level node name (Categories) doesn't appear, I only see directly the first category that is not within Categories.

I'm sure I'm not far from the solution but I've been playing with mu XSLT since a few days and I don't understand what's wrong nor where to correct my mistakes.

A little help to understand would be nice.

Thanks

Vartan
 
Old September 28th, 2007, 03:35 AM
samjudson's Avatar
Friend of Wrox
 
Join Date: Aug 2007
Posts: 2,128
Thanks: 1
Thanked 189 Times in 188 Posts
Default

Couple of problems i could spot.

Firstly, you're not outputting a root element for your XML tree. something like "<OUTPUT>" and "</OUTPUT>" surrounding the apply-template in the plist template would do.

Secondly your key template is of - I can't quite work out why its doing what it is but I've rewritten it below.

Thirdly, some of your input elements are called <false/> (and presumably <true/>) so I added a test to output element name, rather than its contents in these cases.

Code:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/plist">
        <OUTPUT>
            <xsl:apply-templates select="array"/>
        </OUTPUT>
    </xsl:template>

    <xsl:template match="dict">
        <xsl:apply-templates select="key"/>
    </xsl:template>

    <xsl:template match="key">
        <xsl:element name="{translate(text(), ' ', '_')}">
            <xsl:choose>
                <xsl:when test="following-sibling::node()[1][self::array]">
                    <xsl:apply-templates select="following-sibling::node()[1]/dict"/>
                </xsl:when>
                <xsl:when test="following-sibling::node()[1][self::false|self::true]">
                    <xsl:value-of select="following-sibling::node()[1]/local-name()" />
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="following-sibling::node()[1]"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>
/- Sam Judson : Wrox Technical Editor -/
 
Old September 28th, 2007, 12:30 PM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

Comments:

        <xsl:template match="array">
            <xsl:for-each select="dict">
                <xsl:apply-templates select="key"/>
            </xsl:for-each>
        </xsl:template>

that's equivalent to

        <xsl:template match="array">
            <xsl:apply-templates select="dict/key"/>
        </xsl:template>

Similarly

                <xsl:for-each select="../array/dict">
                    <xsl:apply-templates select="key"/>
                </xsl:for-each>

could be <xsl:apply-templates select="../array/dict/key"/>

Finally, Sam's solution has

<xsl:template match="key">
        <xsl:element name="{translate(text(), ' ', '_')}">
            <xsl:choose>
                <xsl:when test="following-sibling::node()[1][self::array]">
                    <xsl:apply-templates select="following-sibling::node()[1]/dict"/>
                </xsl:when>
                <xsl:when test="following-sibling::node()[1][self::false|self::true]">
                    <xsl:value-of select="following-sibling::node()[1]/local-name()" />
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="following-sibling::node()[1]"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:element>
    </xsl:template>

but to me the xsl:choose is crying out to be an apply-templates: <xsl:apply-templates select="following-sibling::*[1]"/> with separate rules for the different things you might find as the next sibling after a <key>.

Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
 
Old September 28th, 2007, 01:51 PM
Registered User
 
Join Date: Sep 2007
Posts: 2
Thanks: 0
Thanked 0 Times in 0 Posts
Send a message via AIM to Vartan
Default

First thank you very much for your quick answers. I still have to study these to understand everything, I'm sure I missed something about apply templates :-) (I thought it wa like a CALL to a function but it seems not.

About Sam's solution, it on ly works with Saxon. I'm on OS X using TestXSLT and I can use Sablotron, LibXSLT, Xalan-J and of course Saxon. I modified the line
Quote:
quote:<xsl:value-of select="following-sibling::node()[1]/local-name()" />
in
Quote:
quote:<xsl:value-of select="local-name(following-sibling::node()[1])" />
and it works with any.

@Mickael.

I'm not sure to understand everything, do you mean replacing the whole xsl:choose ?
Quote:
quote:<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/plist">
        <OUTPUT>
            <xsl:apply-templates select="array"/>
        </OUTPUT>
    </xsl:template>

    <xsl:template match="dict">
        <xsl:apply-templates select="key"/>
    </xsl:template>

    <xsl:template match="key">
        <xsl:element name="{translate(text(), ' ', '_')}">
            <xsl:apply-templates select="following-sibling::*[1]"/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>
It works also, for three levels. If I understand well what you mean, you say to deal with cases. If so how to ? With ifs ? And finally why, as it seems to works without doing so ?

Thanks again for your help

Regards


Vartan
 
Old September 28th, 2007, 03:11 PM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

apply-templates select="X" basically means:

select all the nodes identified by X.

For each one, process it using the template rule that matches it.

So if you see

<xsl:for-each select="X">
  <xsl:choose>
     <xsl:when test="condition1">
        action1
     </xsl:when>
     <xsl:when test="condition2">
        action2
     </xsl:when>
  etc

then you can replace it with

<xsl:apply-templates select="X">

and a set of template rules of the form

<xsl:template match="condition1">
  action1
</xsl:template>

<xsl:template match="condition2">
  action2
</xsl:template>

Why would you want to do this?

Well (a) using templates is the XSLT way of doing things. Like objects and classes in Java, you can ignore them if you like, but you're going against the grain of the language, trying to use it as if it was something else. (b) rule-based processing makes your code more flexible and more reusable. Someone can write another stylesheet that overrides one of your rules but reuses the others. If the data structure is extended, your code is less likely to break.




Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference





Similar Threads
Thread Thread Starter Forum Replies Last Post
XSL for transforming SOAP response doonghati XSLT 1 January 5th, 2008 05:57 AM
Transforming XML to XML using XSL sakreck XSLT 0 January 9th, 2007 11:48 AM
Trouble Transforming an XML SOAP Response with XSL wsessoms XSLT 1 December 20th, 2006 01:47 PM
XSL transforming to TEXT suri_1811 XSLT 8 October 30th, 2006 05:02 AM
Transforming JSP to WML using XSL crispycasper XML 0 May 15th, 2004 12:40 AM





Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Copyright (c) 2020 John Wiley & Sons, Inc.