Joel
Joel

Reputation: 4882

Transform XML to XAML with XSL

Lets say i have the following code snippet in xml:

<PanelWrapper   id="RootPanel" 
                dock="Fill" 
                width="1092" 
                height="605" 
                backcolor="Transparent" 
                visible="True">
    <ButtonWrapper  id="button1" 
                    dock="Left" 
                    text="TestButton" 
                    width="75" 
                    height="605" 
                    border-left="1" 
                    border-top="1" 
                    border-right="1" 
                    border-bottom="1" 
                    font-name="Tahoma" 
                    font-size="9" 
                    font-style="Regular">
    </ButtonWrapper>
</PanelWrapper>

i need to transform the xml code to XAML. The expected endresult should look like this:

<WrapPanel Name="RootPanel" 
           DockPanel.Dock="Left, Right, Top, Bottom" 
           Width="1092" Height="605" 
           Background="Transparent"  
           Visibility="Visible">
    <Button Name="button1" 
            DockPanel.Dock ="Left" 
            Content="TestButton" 
            Width="75" 
            Height="605" 
            BorderThickness="1,1,1,1" 
            FontFamily="Tahoma" 
            FontSize="9" 
            FontStyle="Normal">
    </Button>
</WrapPanel>

is this transformation possible with an xsl stylesheet?

I'm asking especially about transformations like these:

from

border-left="1" 
border-top="1" 
border-right="1" 
border-bottom="1"

to

BorderThickness="1,1,1,1" 

or from

visible="True"

to

Visibility="Visible"

Upvotes: 1

Views: 3342

Answers (1)

Pablo Pozo
Pablo Pozo

Reputation: 1920

This transformation can be done. I am assuming that you are using XSLT 1.0, the solution in XSLT 2.0 would be shorter (because of the recursive template I had to do to achieve 2.). I am assuming also that:

  1. The posible values of Visible are: 'True', 'False' which are transformed to 'Visible' and 'Hidden'.
  2. The BorderThickness attribute is built in the following order: left, top, right, bottom. Besides if some of the border attributes is not found, its value defaults to zero.

The idea of the code is matching manually each attribute and replacing its value to the correct ones. Because I do not know the logic which follow all the attributes (except the bit that I assumed in 1. and 2.) the transformation is limited to the given XML, however I hope it gives you the idea of how to achieve what you are trying to do.

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

    <!-- Ignore attributes by default, so we can have more control
         about unhandled attributes -->
    <xsl:template match="@*" />

    <!-- Transform PanelWrapper to WrapPannel -->
    <xsl:template match="PanelWrapper">
        <WrapPanel>
            <xsl:apply-templates select="@*|*" />
        </WrapPanel>
    </xsl:template>

    <!-- Transform ButtonWrapper to Button -->
    <xsl:template match="ButtonWrapper">
        <Button>
            <xsl:apply-templates select="@*" />
        </Button>
    </xsl:template>

    <!-- Map id attribute to Name -->
    <xsl:template match="@id">
        <xsl:attribute name="Name">
            <xsl:value-of select="." />
        </xsl:attribute>
    </xsl:template>

    <!-- Map @dock = 'Fill' to @dock = 'Left, Right...etc' -->
    <xsl:template match="@dock[. = 'Fill']">
        <xsl:attribute name="DockPanel.Dock">
            <xsl:value-of select="'Left, Right, Top, Bottom'" />
        </xsl:attribute>
    </xsl:template>

    <!-- For every other @dock value, map @dock to DockPanel.dock
         and copy value --> 
    <xsl:template match="@dock">
        <xsl:attribute name="DockPanel.Dock">
            <xsl:value-of select="." />
        </xsl:attribute>
    </xsl:template>

    <!-- Map width attribute to Width -->
    <xsl:template match="@width">
        <xsl:attribute name="Width">
            <xsl:value-of select="." />
        </xsl:attribute>
    </xsl:template>

    <!-- Map height attribute to Height -->
    <xsl:template match="@height">
        <xsl:attribute name="Height">
            <xsl:value-of select="." />
        </xsl:attribute>
    </xsl:template>

    <!-- Map backcolor to Background -->
    <xsl:template match="@backcolor">
        <xsl:attribute name="Background">
            <xsl:value-of select="." />
        </xsl:attribute>
    </xsl:template>

    <!-- Map visible attribute to Visibility -->
    <xsl:template match="@visible[. = 'True']">
        <xsl:attribute name="Visibility">visible</xsl:attribute>
    </xsl:template>

    <xsl:template match="@visible[. = 'False']">
        <xsl:attribute name="Visibility">hidden</xsl:attribute>
    </xsl:template>

    <!-- Map text attribute to content -->
    <xsl:template match="@text">
        <xsl:attribute name="Content">
            <xsl:value-of select="." />
        </xsl:attribute>
    </xsl:template>

    <!-- Build the border attribute -->
    <xsl:template match="@*[starts-with(local-name(), 'border-')][1]">
        <xsl:attribute name="BorderThickness">
            <!-- Print the border-elements in a comma separated list (non-defined attributes default
                 to zero) -->
            <xsl:call-template name="border-print" />
        </xsl:attribute>
    </xsl:template>

    <!-- Recursive template to group borders in BorderThickness -->
    <xsl:template name="border-print">
        <xsl:param name="string" select="'left  top   right bottom'" />
        <xsl:param name="parent" select=".." />
        <xsl:param name="not-first" select="false()" />

        <xsl:if test="$string != ''">
            <!-- Obtain next direction -->
            <xsl:variable name="direction" select="normalize-space(substring($string, 1, 6))" />
            <xsl:variable name="attr" select="$parent/@*[local-name() = concat('border-', $direction)]" />
            <!-- Print comma if not the first element -->
            <xsl:if test="$not-first"><xsl:text>,</xsl:text></xsl:if>
            <!-- Print zero if the attribute cannot be found -->
            <xsl:choose>
                <!-- Attribute found : print -->
                <xsl:when test="$attr">
                    <xsl:value-of select="$attr" />
                </xsl:when>
                <!-- Attribute not found: print 0 -->
                <xsl:otherwise>
                    <xsl:text>0</xsl:text>
                </xsl:otherwise>
            </xsl:choose>
            <!-- Recurse -->
            <xsl:call-template name="border-print">
                <xsl:with-param name="string" select="substring($string, 7)" />
                <xsl:with-param name="parent" select="$parent" />
                <xsl:with-param name="not-first" select="true()" />
            </xsl:call-template>
        </xsl:if>

    </xsl:template>

    <xsl:template match="@*" mode="print-border">
        <xsl:value-of select="concat(., ',')" />
    </xsl:template>

    <xsl:template match="@border-bottom" mode="print-border">
        <xsl:value-of select="." />
    </xsl:template>


    <!-- Map font properties -->
    <xsl:template match="@font-name">
        <xsl:attribute name="FontFamily">
            <xsl:value-of select="." />
        </xsl:attribute>
    </xsl:template>

    <xsl:template match="@font-size">
        <xsl:attribute name="FontSize">
            <xsl:value-of select="." />
        </xsl:attribute>
    </xsl:template>

    <xsl:template match="@font-style[. = 'Regular']">
        <xsl:attribute name="FontStyle">Normal</xsl:attribute>
    </xsl:template>

</xsl:stylesheet>

The main ideas of the code are:

  • You can change the name of an element by using templates like:

    <!-- Transform PanelWrapper to WrapPannel -->
    <xsl:template match="PanelWrapper">
        <WrapPanel>
            <xsl:apply-templates select="@*|*" />
        </WrapPanel>
    </xsl:template>
    

    which matches any element named PanelWrapper and changes its name to WrapPannel.

  • You can change the name of an attribute and keeping its value by using templates like:

    <xsl:template match="@font-size">
        <xsl:attribute name="FontSize">
            <xsl:value-of select="." />
        </xsl:attribute>
    </xsl:template>
    

    which maps the attribute font-size to FontSize.

  • You can change the name and value of an attribute by matching its name and value like:

    <xsl:template match="@font-style[. = 'Regular']">
            <xsl:attribute name="FontStyle">Normal</xsl:attribute>
    </xsl:template>
    

    which matches all attributes such as font-style = 'regular' and transform them to FontStyle = 'normal'.

Upvotes: 5

Related Questions