使用XSL将XML转换为XAML

本文关键字:XAML 转换 XML XSL 使用 | 更新日期: 2023-09-27 18:01:07

假设我在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>

我需要将xml代码转换为XAML。预期的最终结果应该是这样的:

<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>

使用xsl样式表可以进行这种转换吗?

我特别问这样的转换:

来自

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

BorderThickness="1,1,1,1" 

或来自

visible="True"

Visibility="Visible"

使用XSL将XML转换为XAML

可以完成此转换。我假设您使用的是XSLT1.0,XSLT2.0中的解决方案会更短(因为我必须使用递归模板才能实现2(

  1. Visible的可能值为:"True"、"False",它们被转换为"Visible"answers"Hidden">
  2. BorderThickness属性按以下顺序构建:左、上、右、下。此外,如果找不到某些边界属性,则其值默认为零

代码的思想是手动匹配每个属性,并将其值替换为正确的值。因为我不知道所有属性后面的逻辑(除了我在1中假设的位。和2.(转换仅限于给定的XML,但我希望它能让您了解如何实现您想要做的事情

<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>

代码的主要思想是:

  • 您可以使用以下模板更改元素的名称:

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

    它匹配任何名为PanelWrapper的元素,并将其名称更改为WrapPannel。

  • 您可以使用以下模板更改属性的名称并保持其值:

    <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>
    

    它匹配所有属性,如font style="regular",并将它们转换为FontStyle="normal"。