1

同じ階層レベルで繰り返される関連項目を適切にネストされた要素に変換するにはどうすればよいでしょうか?

フォーム エンジンからの複雑な SQL クエリからの出力として、このような XML があり、CV のような情報を収集し、いくつかのフィールド (以前の仕事など) を繰り返すことができます。ただし、それらは友好的な方法で出力されません。

<People>
  <Person>
    <Field1>Fred</Field1>
    <Field2>Head Chef</Field2>
    <Field3>The Ritz Hotel</Field3>
    <Field2>Bottle Washer</Field2>
    <Field3>Dog and Duck</Field3>
  </Person>
  <Person>
    <Field1>Mary</Field1>
    <Field2>Chief Executive</Field2>
    <Field3>BigCorp</Field3>
    <Field2>Manager</Field2>
    <Field3>LargeCorp</Field3>
    <Field2>Mail Clerk</Field2>
    <Field3>SmallCorp</Field3>
  </Person>
</People>

私がする必要があるのは、これを他の XML に変換して、Field2 と Field3 の繰り返しセットを別の親要素に分離することです。

<People>
  <Person>
    <Name>Fred</Name>
    <Jobs>
      <Job>
        <JobTitle>Head Chef</JobTitle>
        <Employer>Ritz Hotel</Employer>
      </Job>
      <Job>
        <JobTitle>Bottle Washer</JobTitle>
        <Employer>Dog and Duck</Employer>
      </Job>
    </Jobs>
  </Person>

  <Person>
    <Name>Mary</Name>
    <Jobs>
      <Job>
        <JobTitle>Chief Executive</JobTitle>
        <Employer>BigCorp</Employer>
      </Job>
      <Job>
        <JobTitle>Manager</JobTitle>
        <Employer>LargeCorp</Employer>
      </Job>
      <Job>
        <JobTitle>Mail Clerk</JobTitle>
        <Employer>SmallCorp</Employer>
      </Job>
    </Jobs>
  </Person>
</People>

フィールドは常に順序付けられます。つまり、Field1 の次に Field2 があり、オプションで別の Field1 と Field2 があり、オプションでうんざりするほど繰り返されます。単発フィールド (もちろん多数あります) の単純な処理を行うコードがありますが、このようなフィールド (学歴、職歴など) の繰り返しセットがいくつかある可能性があります。整理する。翻訳の名前をどこで取得するか (たとえば、"Field3" から "Employer" へ) について心配する必要はありません。解析に役立つ場合は、入力要素の名前を調整できます。たとえば、シーケンスの序数を追加して、関連する要素をグループ化します。たとえば、次のようになります。

...
    <Field2_Seq1>Head Chef</Field2_Seq1>
    <Field3_Seq1>The Ritz Hotel</Field3_Seq1>
    <Field2_Seq2>Bottle Washer</Field2_Seq2>
    <Field3_Seq2>Dog and Duck</Field3_Seq2>
....

私はこれに非常に苦労しています - 私は XSL に不慣れで、それを機能させるように圧力をかけられています (いつ機能しないのですか?)。
ありがとう、キース。

編集: 実際には、繰り返しセット (Field4、Field5... FieldN など) にはさらに多くのフィールドがあり、それらはオプションであるため、シーケンス内の項目を省略した可能性があります: 存在する項目は昇順ですが、必ずしも連続した順序ではありません. 新しい Job 要素のトリガーは、数値が逆になったことです (たとえば、Field6 から Field3 へ)。したがって、対処する必要があります。

...
<Field1>John</Field1>
<Field4>Something</Field4>
<Field6>Missed some more</Field6>
<Field2>New Job</Field2>
...

(部分) のようなものを生成する

...
<Person>
  <Name>John</Name>
  <Job>
    <SomeElement>Something</SomeElement>
    <YetAnother>Missed some more</YetAnother>
  </Job>
  <Job>
    <JobTitle>New Job</JobTitle>
...

不適切な元の質問の例をお詫びします。

4

2 に答える 2

0

順序がわかっている場合は、シーケンスの最初の要素を照合してから、following-siblingを使用して後続の要素を取得できます(次の要素が存在するかどうかわからない場合は、次の要素の名前を確認してください) 。

以下のXSLTは、最初のXMLを最初の出力XMLに変換します。次の兄弟を変更してみてください-兄弟::*[1]を[2]などに変更して、さらに兄弟を取得します。

<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="Person">
        <Person>
            <Name><xsl:value-of select="Field1"/></Name>
            <Jobs>
                <xsl:apply-templates select="Field2"/>
            </Jobs>
        </Person>
    </xsl:template>
    <xsl:template match="Field2">
        <Job>
            <JobTitle><xsl:value-of select="."/></JobTitle>
            <xsl:if test="./following-sibling::*[1][name() = 'Field3']">
                <Employer><xsl:value-of select="./following-sibling::*[1]"/></Employer>
            </xsl:if>
        </Job>
    </xsl:template>
</xsl:stylesheet>
于 2012-06-27T09:02:43.647 に答える
0

この XSLT 1.0 スタイルシートはうまくいくはずです...

<?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" indent="yes"/>

<xsl:template match="/">
 <People>
   <xsl:apply-templates select="People/Person"/>
 </People>
</xsl:template>

<xsl:template match="Person">
 <xsl:copy>
  <Name><xsl:value-of select="Field1"/></Name>
  <jobs>
   <xsl:for-each select="*[not( substring( local-name( preceding-sibling::*
                     [substring( local-name(), 6) &gt; 1][1]),6) &lt;
                    substring( local-name(), 6))
                    and substring( local-name(), 6) &gt; 1]">
    <job>
      <xsl:variable name="id-of-head" select="generate-id()" />
      <xsl:apply-templates select="
       .|(following-sibling::*[
         $id-of-head = generate-id(
      preceding-sibling::*[not( substring( local-name( preceding-sibling::*
                     [substring( local-name(), 6) &gt; 1][1]),6) &lt;
                    substring( local-name(), 6))][1])]
      [substring( local-name( preceding-sibling::*
                     [substring( local-name(), 6) &gt; 1][1]),6) &lt;
                    substring( local-name(), 6)])" />
    </job>  
   </xsl:for-each> 
  </jobs>
 </xsl:copy>
</xsl:template>

<xsl:template match="Field2" priority="1">
  <JobTitle><xsl:value-of select="." /></JobTitle> 
</xsl:template>

<xsl:template match="Field3" priority="1">
  <Employer><xsl:value-of select="." /></Employer> 
</xsl:template>

<xsl:template match="*[substring( local-name(), 6) &gt; 1]">
  <xsl:copy>
    <xsl:value-of select="." />
    <xsl:comment>You haven't defined a translation for this field yet.
To do so, just add another template.
See templates for Fields 2 and 3 for example.</xsl:comment>
  </xsl:copy> 
</xsl:template>

</xsl:stylesheet>

キーで同等のことを行うスマートな方法があるはずです。XSLT 2.0 を使用できるかどうかお知らせください。これにより、ソリューションが大幅に小さくシンプルになります。

アップデート

このスタイルシートをキーを使用する形式に変換したので、もう少し読みやすくなりました。それがムエンチャンまたは準ムエンチャンの資格があるかどうかはわかりませんか?ここは...

<?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" indent="yes"/>

<xsl:key name="job"
  match="
  Person/*
    [starts-with(local-name(),'Field')]
    [substring( local-name(), 6) &gt; 1]"
  use="
  generate-id( (.|preceding-sibling::*)
    [starts-with(local-name(),'Field')]
    [substring( local-name(), 6) &gt; 1]
    [
     (position() = 1)
      or
     ( substring( local-name(), 6)
        &lt;
     substring(
       local-name( preceding-sibling::*
       [starts-with(local-name(),'Field')]
       [1]
           ), 6
          )
     )
    ]
    [last()])"
  />

<xsl:template match="/">
 <People>
   <xsl:apply-templates select="People/Person"/>
 </People>
</xsl:template>

<xsl:template match="Person">
 <xsl:copy>
  <Name><xsl:value-of select="Field1"/></Name>
  <jobs>
   <xsl:for-each select="
    *
    [starts-with(local-name(),'Field')]
    [substring( local-name(), 6) &gt; 1]
    [
     (position() = 1)
        or
     ( substring( local-name(), 6)
        &lt;
       substring(
       local-name( preceding-sibling::*
         [starts-with(local-name(),'Field')]
         [1]
             ), 6
          )
      )
     ]">
    <job>
    <xsl:apply-templates select="key('job',generate-id(.))" />
  </job>
   </xsl:for-each>
  </jobs>
 </xsl:copy>
</xsl:template>

<xsl:template match="Field2" priority="1">
  <JobTitle><xsl:value-of select="." /></JobTitle>
</xsl:template>

<xsl:template match="Field3" priority="1">
  <Employer><xsl:value-of select="." /></Employer>
</xsl:template>

<xsl:template match="*[substring( local-name(), 6) &gt; 1]">
  <xsl:copy>
    <xsl:value-of select="." />
    <xsl:comment>You haven't defined a translation for this field yet.
To do so, just add another template.
See templates for Fields 2 and 3 for example.</xsl:comment>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>
于 2012-06-27T15:37:58.887 に答える