65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 XML/XSLT 与 "C 预处理器"

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.52/5 (6投票s)

2007年5月7日

CPOL

3分钟阅读

viewsIcon

43338

downloadIcon

96

如何滥用 XML/XSL 来做即使是

引言

这很可能是在这里最不受欢迎的文章。它是由 "编码噩梦" 帖子引发的 论坛

(本文中的示例实际上比发布的示例更简单。)

它唯一的优点在于如果有人从中了解了一些关于 XML 和 XSLT 的知识。

背景

纯粹的 C 有一个 .NET(特别是 Common Type System)没有的限制。在这方面,这篇文章根本不应该出现在 Code Project 上,它实际上与 .NET 无关。

这种特殊的限制与 enum 有关。在 .NET 中,获取枚举值的文本表示非常简单。在这个 C# 例子中,我定义了一个 enum,将它的一个值分配给一个变量,并将其显示到控制台。请注意,我实际上必须努力才能获得 enum 成员的数值。

CSenum.cs

namespace CSenum
{
    public partial class
    CSenum
    {
        public enum UserEnum
        {
            Abe
        ,
            Bob
        ,
            Cat
        ,
            Deb
        }

        public struct
        Message
        {
            public UserEnum From ;
            public string Text ;
        }

        [System.STAThreadAttribute]
        public static int
        Main
        (
            string[] args
        )
        {
            try
            {
                if ( args.Length > 0 )
                {
                    Message m ;

                    m.From = UserEnum.Bob ;
                    m.Text = args [ 0 ] ;

                    System.Console.WriteLine ( "{0} says \"{1}\"" , 
                        (int) m.From , m.Text ) ;
                    System.Console.WriteLine ( "{0} says \"{1}\"" , 
                        m.From , m.Text ) ;
                }
            }
            catch ( System.Exception err )
            {
                System.Console.Write ( err.Message ) ;
            }

            return ( 0 ) ;
        }
    }
}
结果
C:\>CSenum Hello
1 says "Hello"
Bob says "Hello"

现在这里有一个 C 版本,它演示了这个限制。枚举值无法转换为其文本表示。Cenum1.c

# include 

typedef enum
{
    Abe
,
    Bob
,
    Cat
,
    Deb
,
    MAX  /* Helps to bounds-check the value */
} UserEnum ;

typedef struct
{
    UserEnum From ;
    char*    Text ;
} Message ;

int
main
(
    int   argc
,
    char* argv[]
)
{
    int result = 0 ;

    if ( argc > 1 )
    {
        Message m ;

        m.From = Bob ;
        m.Text = argv [ 1 ] ;
       
        printf ( "%d says \"%s\"\n" , m.From , m.Text ) ;

        /* This line would cause an error */
//      printf ( "%s says \"%s\"\n" , m.From , m.Text ) ;
    }

    return ( result ) ;
}

结果

C:\>Cenum1 Hello
1 says "Hello"

所以,我们要做的是添加一个字符串数组

Cenum2.c

# include <stdio.h>

typedef enum
{
    Abe
,
    Bob
,
    Cat
,
    Deb
,
    MAX  /* Helps to bounds-check the value */
} UserEnum ;

char *UserNames[] =
{
    "Abe"
,
    "Bob"
,
    "Cat"
,
    "Deb"
} ;

typedef struct
{
    UserEnum From ;
    char*    Text ;
} Message ;

int
main
(
    int   argc
,
    char* argv[]
)
{
    int result = 0 ;

    if ( argc > 1 )
    {
        Message m ;

        m.From = Bob ;
        m.Text = argv [ 1 ] ;
       
        printf ( "%d says \"%s\"\n" , m.From , m.Text ) ;

        /* This line would cause an error */
//      printf ( "%s says \"%s\"\n" , m.From , m.Text ) ;

        /* We use the numeric value as an index into the array of strings */
        printf ( "%s says \"%s\"\n" , UserNames [ m.From ] , m.Text ) ;
    }

    return ( result ) ;
}

结果

C:\>Cenum2 Hello
1 says "Hello"
Bob says "Hello"

通常,enum、字符串数组和 struct 的定义将在一个或多个头文件 (h) 中。目前,我只分离出 enum 和字符串数组。

Cenum3.h

typedef enum
{
    Abe
,
    Bob
,
    Cat
,
    Deb
,
    MAX  /* Helps to bounds-check the value */
} UserEnum ;

char *UserNames[] =
{
    "Abe"
,
    "Bob"
,
    "Cat"
,
    "Deb"
} ;

在 C 文件中,在删除以上内容后,添加

来自 Cenum3.c

# include "Cenum3.h"

将此类定义从使用它们的代码中分离出来,可以更容易地维护 C 程序。但我们剩下两个相关的定义,必须一起维护。可能出现的一些潜在问题是

  1. enum 添加一个值,但未添加到数组
  2. 将一个字符串添加到数组中的错误位置

显然,找到一种只维护 一个 列表的方法是可取的。Coding Horrors 帖子中讨论的代码就是这样一种方法,并且它有效,但它相当可怕。

使用 XML 和 XSLT 达到同样的结果

警告:我即将描述的技术只是“稍微”不那么可怕。C 纯粹主义者不应该继续。

我将使用 XML 来存储列表。这样的 XML 文档几乎可以按照你喜欢的方式编写(遵守格式良好的规则)。

格式良好的 XML 文档包括

<User>
  <Abe/>
  <Bob/>
  <Cat/>
  <Deb/>
</User>

<Enum>
  <Name>User</Name>
  <Value>Abe</Value>
  <Value>Bob</Value>
  <Value>Cat</Value>
  <Value>Deb</Value>
</Enum>

<Enum Name="User">
  <Value Name="Abe"/>
  <Value Name="Bob"/>
  <Value Name="Cat"/>
  <Value Name="Deb"/>
</Enum>

我更喜欢第一个,如果仅仅是出于简洁的原因。第三个示例中使用属性似乎也普遍不受欢迎。这样的文件可能是数据库查询或其他进程的输出。

现在我们想要编写一个 XSL 文档,将 XML 文档转换为 Cenum3.h(结果头文件的确切格式并不重要,只要编译器理解它即可。)

Cenum3.xsl

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
  <xsl:output omit-xml-declaration="yes" method="text"/>
  <xsl:template match="/">
    <xsl:for-each select="*">
      typedef enum {
      <xsl:for-each select="*">
    <xsl:value-of select="name()"/>,
        <xsl:if test="position()=last()">MAX</xsl:if>
      </xsl:for-each>} <xsl:value-of select="name()"/>Enum ;
      char *<xsl:value-of select="name()"/>Names[] = {
      <xsl:for-each select="*">
    <xsl:if test="position()!=1">,</xsl:if>&quot;<xsl:value-of 
      select="name()"/>&quot;
      </xsl:for-each>} ;
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

我不会详细介绍 XSL 的作用;我只想说它会迭代 User 元素的子元素两次。首先它创建 enum 的定义,然后它创建字符串数组的定义。

在此处学习 XSL here

结果 Cenum3.h

      typedef enum {
      Abe,
        Bob,
        Cat,
        Deb,
        MAX} UserEnum ;
      char *UserNames[] = {
      "Abe"
      ,"Bob"
      ,"Cat"
      ,"Deb"
      } ;

现在你可能会问自己,“我怎么到这里的?” 意思是,“有什么实用程序可以获取 XML 和 XSL 并实际执行转换?” 好吧,这里有一个非常简单的控制台程序来做到这一点

SimpleXSLT.cs

namespace SimpleXSLT
{
    public partial class SimpleXSLT
    {
        [System.STAThreadAttribute]
        public static int
        Main
        (
            string[] args
        )
        {
            int result = 0 ;

            try
            {
                System.Xml.Xsl.XslCompiledTransform xslt = new 
                    System.Xml.Xsl.XslCompiledTransform() ;

                if ( args.Length == 2 )
                {
                    xslt.Load ( args [ 1 ] ) ;

                    xslt.Transform
                    (
                        args [ 0 ]
                    ,
                        null
                    ,
                        System.Console.Out
                    ) ;
                }
                else
                {
                    System.Console.Write ( "Syntax: XSLT xmlfile xslfile" ) ;
                } 
            }
            catch ( System.Exception err )
            {
                System.Console.Write ( err.Message ) ;
            }

            return ( result ) ;
        }
    }
}

结果

C:\>SimpleXSLT Cenum3.xml Cenum3.xsl

      typedef enum {
      Abe,
        Bob,
        Cat,
        Deb,
        MAX} UserEnum ;
      char *UserNames[] = {
      "Abe"
      ,"Bob"
      ,"Cat"
      ,"Deb"
      } ;

(你可以将输出重定向到一个文件。)

C:\>SimpleXSLT Cenum3.xml Cenum3.xsl > Cenum3.h

历史

2007-05-02 原始版本

© . All rights reserved.