XSLT вопрос. Как связать теги полей с данными, если исходный XML содержит их в отдельных разделах? - PullRequest
2 голосов
/ 13 июля 2009

Надеюсь, я никого не потеряю, упомянув 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 там. :) Другой вопрос, который у меня возник, заключался в том, как отформатировать числовую цену, когда оригинал хранится как текст (00009.99000000) в XML в валюте $ 9,99, но я могу работать над этим.

Ответы [ 3 ]

6 голосов
/ 13 июля 2009

Один элегантный способ решить эту проблему:

<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> для создания элементов с динамическим именем
  • использование оси XPath preceding-sibling в качестве способа определения положения узла (поскольку функция position() не работает в <xsl:key> s

Продолжайте и спросите, если что-то неясно.

Ваша другая проблема (форматирование чисел) имеет ответ здесь: XSL: Форматирование чисел, исключая конечные нули .

1 голос
/ 13 июля 2009

Это должно помочь вам начать:

<?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 () .

1 голос
/ 13 июля 2009

Вы можете перейти обратно в корень, запустив запрос 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>
...