0

各料理のすべての条件と測定値を組み合わせるには、次をどのように変換しますか? 繰り返し子要素を組み合わせるのに苦労しています。

<?xml version="1.0" encoding="UTF-8"?>
<Test>
  <Experiment id='1'>
    <Dish1>
      <Conditions pressure='x' temp='y'/>
      <Measurement timeStamp='8am' reading='y'/>
    </Dish1>
    <Dish2>
      <Conditions pressure='x' temp='y'/>
      <Measurement timeStamp='8am' reading='y'/>
    </Dish2>
    <Dish1>
      <Conditions pressure='x' temp='y'/>
      <Measurement timeStamp='2pm' reading='y'/>
    </Dish1>
    <Dish2>
      <Conditions pressure='x' temp='y'/>
      <Measurement timeStamp='2pm' reading='y'/>
    </Dish2>
   </Experiment>
   <Experiment id='2'>
    <Dish1>
      <Conditions pressure='x' temp='y'/>
      <Measurement timeStamp='9am' reading='y'/>
    </Dish1>
    </Experiment>
    <Experiment id='2'>
      <Dish1>
        ...
</Test>

望ましい結果:

<?xml version="1.0" encoding="UTF-8"?>
<Test>
   <Experiment id='1'>
     <Dish1>
        <Observation pressure='x' temp='y' timeStamp='8am' reading='y'/>
        <Observation pressure='x' temp='y' timeStamp='2pm' reading='y'/>
     </Dish1>
     <Dish2>
        <Observation pressure='x' temp='y' timeStamp='8am' reading='y'/>
        <Observation pressure='x' temp='y' timeStamp='2pm' reading='y'/>
     </Dish2>
   </Experiment>
   <Experiment id='2'>
     <Dish1>
         <Observation pressure='x' temp='y' timeStamp='9am' reading='y'/>
          ...

よろしくお願いします!

これが私がこれまでに試したことです:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:template match="Experiment">
        <xsl:copy>
            <xsl:for-each select="Dish1">
                <xsl:element name="Observation">
                    <xsl:attribute name="pressure"><xsl:value-of select="Conditions/@pressure"/></xsl:attribute>
                    <xsl:attribute name="temp"><xsl:value-of select="Conditions/@temp"/></xsl:attribute>
                    <xsl:attribute name="TimeStamp"><xsl:value-of select="Measurement/@TimeStamp"/></xsl:attribute>
                    <xsl:attribute name="reading"><xsl:value-of select="Measurement/@reading"/></xsl:attribute>
                </xsl:element>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>
    <!-- copy everthing not covered above-->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

失敗した試みを書き留めているうちに、上記の変換を思いつきました。動作しているようですが、出力を検証する必要があります。提案/改善をいただければ幸いです。ありがとうございました。

...私の変換は、最初の実験でのみ機能します。追加すると:

<xsl:template match="Experiment">
  <xsl:copy>
  <xsl:for-each select="Dish2">
  <xsl:element name="Observation">
  <xsl:attribute name="pressure"><xsl:value-of select="Conditions/@pressure"/></xsl:attribute>
  <xsl:attribute name="temp"><xsl:value-of select="Conditions/@temp"/></xsl:attribute>
  <xsl:attribute name="TimeStamp"><xsl:value-of select="Measurement/@TimeStamp"/></xsl:attribute>
  <xsl:attribute name="reading"><xsl:value-of select="Measurement/@reading"/></xsl:attribute>
  </xsl:element>
  </xsl:for-each>
  </xsl:copy>
</xsl:template>

...Dish2 要素が見つかりません。

Ian Roberts に感謝します。失敗した試行を書き留めてもらうことで、アイデアが生まれ、以下の実用的なソリューションにつながりました。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:template match="Experiment">
        <xsl:copy>
            <xsl:for-each select="Dish1">
                <xsl:element name="Observation">
                    <xsl:attribute name="pressure"><xsl:value-of select="Conditions/@pressure"/></xsl:attribute>
                    <xsl:attribute name="temp"><xsl:value-of select="Conditions/@temp"/></xsl:attribute>
                    <xsl:attribute name="TimeStamp"><xsl:value-of select="Measurement/@TimeStamp"/></xsl:attribute>
                    <xsl:attribute name="reading"><xsl:value-of select="Measurement/@reading"/></xsl:attribute>
                </xsl:element>
            </xsl:for-each>
            <xsl:for-each select="Dish2">
                <xsl:element name="Observation">
                    <xsl:attribute name="pressure"><xsl:value-of select="Conditions/@pressure"/></xsl:attribute>
                    <xsl:attribute name="temp"><xsl:value-of select="Conditions/@temp"/></xsl:attribute>
                    <xsl:attribute name="TimeStamp"><xsl:value-of select="Measurement/@TimeStamp"/></xsl:attribute>
                    <xsl:attribute name="reading"><xsl:value-of select="Measurement/@reading"/></xsl:attribute>
                </xsl:element>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>
    <!-- copy everthing not covered above-->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>
4

1 に答える 1

2

完全に柔軟にするには、Dish1Dish2のハードコーディングを避け、任意の数の料理を許可することをお勧めします。'dish' 要素を実験 ID と料理名で効果的にグループ化する必要があります。

XSLT 1.0 では、これに Muenchian Grouping と呼ばれる手法を使用します。まず、料理の要素をグループ化するためのキーを定義します

<xsl:key name="Dish" match="Experiment/*" use="concat(../@id, '-', local-name())" />

次に、実験用の一意の「料理」要素を取得するには、その名前のグループで最初に発生する料理要素を選択する必要があります

<xsl:apply-templates 
    select="*[generate-id() = generate-id(key('Dish', concat(../@id, '-', local-name()))[1])]" />

次に、このグループ内のすべての料理を取得するには、次のように選択します。

<xsl:apply-templates select="key('Dish', concat(../@id, '-', local-name()))" mode="group" />

'Dish' 要素の子要素を組み合わせるには、単一のテンプレートが必要です。

<xsl:template match="Experiment/*" mode="group">
    <Observation>
        <xsl:apply-templates select="*/@*" />
    </Observation>
</xsl:template>

ここに完全な XSLT があります

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

   <xsl:key name="Dish" match="Experiment/*" use="concat(../@id, '-', local-name())" />

   <xsl:template match="Experiment">
      <xsl:copy>
         <xsl:apply-templates select="@*" />
         <xsl:apply-templates select="*[generate-id() = generate-id(key('Dish', concat(../@id, '-', local-name()))[1])]" />
      </xsl:copy>
   </xsl:template>

   <xsl:template match="Experiment/*">
      <xsl:copy>
         <xsl:apply-templates select="key('Dish', concat(../@id, '-', local-name()))" mode="group" />
      </xsl:copy>
   </xsl:template>

   <xsl:template match="Experiment/*" mode="group">
      <Observation>
         <xsl:apply-templates select="*/@*" />
      </Observation>
   </xsl:template>

   <xsl:template match="@*|node()">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
   </xsl:template>
</xsl:stylesheet>

ここでのモードの使用に注意してください。XSLT にはExperimentの子要素に一致する 2 つのテンプレートが含まれているため、XSLT はそれらを区別する必要があります。(1 つ目は、個別の「料理」名を出力することです。2 つ目は、その名前を持つすべての要素を照合して処理し、子を結合するために使用されます)。

(切り捨てられた) XML サンプルに適用すると、以下が出力されます。

<Test>
   <Experiment id="1">
      <Dish1>
         <Observation pressure="x" temp="y" timeStamp="8am" reading="y"/>
         <Observation pressure="x" temp="y" timeStamp="2pm" reading="y"/>
      </Dish1>
      <Dish2>
         <Observation pressure="x" temp="y" timeStamp="8am" reading="y"/>
         <Observation pressure="x" temp="y" timeStamp="2pm" reading="y"/>
      </Dish2>
   </Experiment>
   <Experiment id="2">
      <Dish1>
         <Observation pressure="x" temp="y" timeStamp="9am" reading="y"/>
      </Dish1>
   </Experiment>
</Test>

XSLT 2.0 を使用できる場合は、xsl:for-each-groupが新しい親友になります。この XSLT for XSLT 2.0 を試してください

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>

   <xsl:template match="Experiment">
      <xsl:copy>
         <xsl:apply-templates select="@*" />
         <xsl:for-each-group select="*" group-by="local-name()">
            <xsl:copy>
               <xsl:apply-templates select="current-group()" />
            </xsl:copy>
         </xsl:for-each-group>
      </xsl:copy>
   </xsl:template>

   <xsl:template match="Experiment/*">
      <Observation>
         <xsl:apply-templates select="*/@*" />
      </Observation>
   </xsl:template>

   <xsl:template match="@*|node()">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
   </xsl:template>
</xsl:stylesheet>

どちらの場合も、Dish1Dishについて言及されていないことに注意してください。そのため、XSLT を変更しなくても、 Dish3以上を簡単に作成できます。

于 2013-04-08T22:37:03.747 に答える