XSLT问题。当原始XML在不同的部分中将字段标记与数据配对时,如何将其与数据配对

时间:2009-07-13 18:16:21

标签: xml xslt translation filemaker

我希望通过提及Filemaker,我不会失去任何人。我试图将它的XML导出转换为SSIS可用的东西。 FM的原生XML导出在同一XML文件的不同部分中具有字段名称和数据。这列出了我需要它做什么,我目前做了什么,以及底部的原始FM导出以供参考。我今天早上还没有看过XML翻译,所以请耐心等待:D。我可以根据需要发布更多信息。

<!-- What we actually want example -->
<?xml version="1.0" encoding="UTF-8"?>

<PRODUCTRECS>
<PRODUCT>
    <name>Dr. Zim</name>
    <address>1234 Internet Way</address>
    <city/><state/><zip/>
</PRODUCT>
...
</PRODUCTRECS>

有没有办法让XSLt读取顶部的字段名称,并在翻译时将字段名称放在实际数据周围?目前,我只是选择IF语句这样的位置(这有效但非常依赖和混乱):

<!-- Current nightmare code, check for each individually and print it out -->
<xsl:template match="fmp:FMPXMLRESULT">
<PRODUCTRECS>
<xsl:for-each select="fmp:RESULTSET/fmp:ROW">
    <PRODUCT>
<xsl:for-each select="fmp:COL">
<xsl:if test="position()=1">
    <name><xsl:value-of select="fmp:DATA"/></name>
</xsl:if>
...
</xsl:for-each>
    </PRODUCT>
</xsl:for-each>
</PRODUCTRECS>
</xsl:template>

这是Filemaker默认输出的内容:

<?xml version="1.0" encoding="UTF-8" ?>
<FMPXMLRESULT xmlns="http://www.filemaker.com/fmpxmlresult">
<ERRORCODE>0</ERRORCODE>
<PRODUCT BUILD="01-01-2009" NAME="FileMaker Pro" VERSION="10.0v3"/>
<DATABASE DATEFORMAT="M/d/yyyy" LAYOUT="" NAME="filename.fp7" RECORDS="10" TIMEFORMAT="h:mm:ss a"/>
<METADATA>
  <FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="name" TYPE="TEXT"/>
  <FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="address" TYPE="TEXT"/>
  <FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="city" TYPE="TEXT"/>
  <FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="state" TYPE="TEXT"/>
  <FIELD EMPTYOK="YES" MAXREPEAT="1" NAME="zip" TYPE="TEXT"/>
</METADATA>
<RESULTSET FOUND="10">
<ROW MODID="0" RECORDID="1">
  <COL><DATA>Dr. Zim</DATA></COL>
  <COL><DATA>1234 Internet Way</DATA></COL>
  <COL><DATA></DATA></COL>
  <COL><DATA></DATA></COL>
  <COL><DATA></DATA></COL>
  ...
</ROW>
...
</RESULTSET>
</FMPXMLRESULT>

期待着那里的大师XSLTers。 :)我的另一个问题是当原始文件在XML中存储为文本(00009.99000000)时,如何格式化数字价格为9.99美元的货币,但我可以处理这个。

3 个答案:

答案 0 :(得分:6)

解决这个问题的一个优雅方法是:

<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:fmp="http://www.filemaker.com/fmpxmlresult"
  exclude-result-prefixes="fmp"
>

  <!-- the key indexes the METADATA fields by their position -->
  <xsl:key 
    name="kMetaData" 
    match="fmp:METADATA/fmp:FIELD" 
    use="count(preceding-sibling::fmp:FIELD) + 1" 
  />

  <!-- separate templates increase readability -->
  <xsl:template match="/fmp:FMPXMLRESULT">
    <PRODUCTRECS>
      <xsl:apply-templates select="fmp:RESULTSET/fmp:ROW" />
    </PRODUCTRECS>
  </xsl:template>

  <xsl:template match="fmp:ROW">
    <PRODUCT>
      <xsl:apply-templates select="fmp:COL" />
    </PRODUCT>
  </xsl:template>

  <xsl:template match="fmp:COL">
    <!-- column name lookup is high-speed because of the key -->
    <xsl:element name="{string(key('kMetaData', position())/@NAME)}">
      <xsl:value-of select="fmp:DATA" />
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

我系统上的哪些输出:

<PRODUCTRECS>
  <PRODUCT>
    <name>Dr. Zim</name>
    <address>1234 Internet Way</address>
    <city></city>
    <state></state>
    <zip></zip>
  </PRODUCT>
</PRODUCTRECS>

但是,请注意,XML元素名称受制于比FileMaker列名更严格的规则。如果您的列名违反这些规则,上述内容将会崩溃并烧毁。

样式表的显着特征是:

  • <xsl:key>用于快速查找节点 - 对于较大的输入,这应该会变得明显
  • exclude-result-prefixes以防止在结果中声明fmp命名空间
  • <xsl:element>创建具有动态名称的元素
  • 使用preceding-sibling XPath轴作为确定节点位置的方法(因为position()函数在<xsl:key>中不起作用

继续询问是否有任何不清楚的地方。

您的其他问题(数字格式)在此处有一个答案:XSL: Formatting numbers, excluding trailing zeroes

答案 1 :(得分:1)

您可以使用/跳回到启动xpath查询的根目录 即/fmp:FMPXMLRESULT/fmp:METADATA/fmp:FIELD

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
        xmlns:fmp="http://www.filemaker.com/fmpxmlresult"
    >
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="/fmp:FMPXMLRESULT">
        <PRODUCTRECS>
            <xsl:apply-templates select="fmp:RESULTSET/fmp:ROW" />
        </PRODUCTRECS>
    </xsl:template>

    <xsl:template match="fmp:RESULTSET/fmp:ROW">
        <PRODUCT>
            <xsl:apply-templates select="fmp:COL" />
        </PRODUCT>
    </xsl:template>

    <xsl:template match="fmp:COL">
        <xsl:variable name="currentPosition" select="position()" />
        <xsl:element name="{/fmp:FMPXMLRESULT/fmp:METADATA/fmp:FIELD[position() = $currentPosition]/@NAME}">
            <xsl:value-of select="fmp:DATA"/>
        </xsl:element>
    </xsl:template>


</xsl:stylesheet>

答案 2 :(得分:1)

这应该让你开始:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:fm="http://www.filemaker.com/fmpxmlresult">
    <xsl:template match="/fm:FMPXMLRESULT">
        <PRODUCTRECS>
            <xsl:apply-templates select="fm:RESULTSET/fm:ROW"/>
        </PRODUCTRECS>
    </xsl:template>

    <xsl:template match="fm:ROW">
        <PRODUCT>
        <!--
            Use this if the element containing the NAME="FileMaker Pro" attribute is the one you want to use
            for each row name.
            <xsl:element name="{name(/fm:FMPXMLRESULT/*[@NAME='FileMaker Pro'])}">-->
            <xsl:for-each select="fm:COL/fm:DATA">
                <xsl:variable name="currentPos" select="position()"/>
                <xsl:element name="{/fm:FMPXMLRESULT/fm:METADATA/fm:FIELD[position()=$currentPos]/@NAME}">
                    <xsl:value-of select="."/>
                </xsl:element>
            </xsl:for-each>
        <!--</xsl:element>-->
        </PRODUCT>
    </xsl:template>
</xsl:stylesheet>

另外,请查看xsl:number的其他部分,或者只看number() function