2017-05-19 3 views
1

私はXSLTの初心者です.XSLT 1.0を使用してXML文書を変換するサードパーティのソフトウェアにxsltスクリプトを挿入する必要があります。XSLT 1.0を使用して元の要素を上書きせずに2つの文書をマージする方法はありますか?

私の仕事は、文書A.xmlを取って、文書B.xmlから各要素を挿入することですが、A内のテキストがまだ存在しない場合に限ります。出力は文書C.xmlとして生成されます。

例A.xml:

<?xml version="1.0" encoding="UTF-8"?> 
<metadata> 
    <Table> 
     <Name>SCHAME.table_name</Name> 
     <Location>oracle:TNS_1</Location> 
     <Citation> 
      <Title>Title 1</Title> 
      <Description/> 
     </Citation> 
     <metadataDate>20170418</metadataDate> 
    </Table> 
</metadata> 

例B.xml:

<?xml version="1.0" encoding="UTF-8"?> 
<metadata> 
    <Table> 
     <Citation> 
      <Title>Template Title</Title> 
      <Abstract>Template Abstract</Abstract> 
      <Description>Template Description</Description> 
     </Citation> 
     <MetadataDate>20160131</MetadataDate> 
    </Table> 
</metadata> 

C.xmlの予想される出力は次のとおりです。

<?xml version="1.0" encoding="UTF-8"?> 
<metadata> 
    <Table> 
     <Name>SCHAME.table_name</Name> 
     <Location>oracle:TNS_1</Location> 
     <Citation> 
      <Title>Title 1</Title> 
      <Abstract>Template Abstract</Abstract> 
      <Description>Template Description</Description> 
     </Citation> 
     <metadataDate>20170418</metadataDate> 
    </Table> 
</metadata> 

3つのことが重要です。

  1. BはAには存在しないが、Cにコピーしなければならない要素を含むことができる(例えば、メタデータ/表/引用/要約)
  2. BのテキストでAの要素を上書きしてはならない。メタデータ/テーブル/引用/タイトル)。また、Aの空の要素は、Bのテキストで埋め尽くさなければなりません。
  3. xmlは単なるサンプルであり、実際のXMLファイルには100以上の異なるタグがあります。私の問題を描いている。だから私の問題の解決策は、私のサンプルXMLにあるものより多くのタグに適用可能でなければなりません。

私は実行中の解決策は必要ありませんが、XSLTの初心者のためにこれを解決する方法はいいと思います。

+0

このタスクを総称して実行する必要がありますか、または目的の出力に既知の形式を使用できますか。つまり、すべての可能な要素が表現された、目的の出力のための徹底的な塗りつぶし可能なテンプレートとして役立つXMLファイルを書くことは可能でしょうか? –

+0

xmlは地理データのINSPIREメタデータに準拠しているため、一般的にタスクを実行したいと思います。しかし、私たちの顧客は実際には完全な仕様のより小さなサブセットを必要としているので、私は顧客要件のために徹底した塗り潰し可能なテンプレートを作成すると思います。この質問を決めるのにもう時間を費やすつもりです。 http://inspire.ec.europa.eu/documents/inspire-metadata-implementing-rules-technical-guidelines-based-en-iso-19115-and-en-iso-1 –

答えて

0

ここでの問題は、両方のファイルでに対応するを識別する方法です。私は各要素が最大で1回出現すると仮定しているので、要素名だけで対応する要素を識別することができます。所望の出力構造のすべて可能な要素を含むtemplate.xmlファイルを作成します

  1. : 私のソリューションは、ジョン・ボリンジャーの考え方に従っています。
  2. XSLスクリプトはテンプレートのすべての要素を実行します。
  3. これは、これは私が使用したテンプレートであるB.
  4. から値を取る

他に、値Aが空でない場合は、ファイルAとB

  • でこの要素の値は、それを取るルックアップの:

    <?xml version="1.0"?> 
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
    
        <xsl:output method="xml" indent="yes" />   
        <xsl:strip-space elements="*"/> 
    
        <!-- Parameters --> 
        <xsl:param name="aFile"  select="'a.xml'" /> 
        <xsl:param name="bFile"  select="'b.xml'" /> 
    
        <!-- Variables --> 
        <xsl:variable name="aDoc" select="document($aFile, .)"/> 
        <xsl:variable name="bDoc" select="document($bFile, .)"/> 
    
        <!-- Locate elements by name in both files --> 
        <xsl:key name="elementsByName" match="*" use="name()" /> 
    
        <!-- Root-Template --> 
        <xsl:template match="/"> 
         <xsl:comment> 
          <xsl:value-of select="concat('Merge of ', $aFile, ' with ', $bFile)" /> 
         </xsl:comment> 
    
         <xsl:apply-templates /> 
        </xsl:template> 
    
        <!-- Merge all elements --> 
        <xsl:template match="*"> 
         <xsl:variable name="elemName" select="name()" /> 
         <xsl:variable name="aValue" select="$aDoc/key('elementsByName', $elemName)/text()" /> 
         <xsl:variable name="bValue" select="$bDoc/key('elementsByName', $elemName)/text()" /> 
    
         <xsl:copy> 
          <xsl:choose> 
           <xsl:when test="$aValue != ''"> 
            <xsl:value-of select="$aValue" /> 
           </xsl:when> 
    
           <xsl:when test="$bValue != ''"> 
            <xsl:value-of select="$bValue" /> 
           </xsl:when> 
          </xsl:choose> 
    
          <xsl:apply-templates select="*" /> 
         </xsl:copy> 
        </xsl:template> 
    
    </xsl:stylesheet> 
    

    transformatiの結果:

    <?xml version="1.0" encoding="UTF-8"?> 
    <metadata> 
        <Table> 
         <Name/> 
         <Location/> 
         <Citation> 
          <Title /> 
          <Abstract /> 
          <Description/> 
         </Citation> 
         <metadataDate/> 
        </Table> 
    </metadata> 
    

    次のXSLは、入力として、上記テンプレートを有しますである:

    <?xml version="1.0" encoding="UTF-8"?> 
    <!--Merge of a.xml with b.xml--> 
    <metadata> 
        <Table> 
         <Name>SCHAME.table_name</Name> 
         <Location>oracle:TNS_1</Location> 
         <Citation> 
         <Title>Title 1</Title> 
         <Abstract>Template Abstract</Abstract> 
         <Description>Template Description</Description> 
         </Citation> 
         <metadataDate>20170418</metadataDate> 
        </Table> 
    </metadata> 
    

    このコードでは唯一の「トリック」xsl:keyの使用とそれに対応するkey()機能です。これは は、このスクリプトをコピーし、出力ファイルC. へのテンプレートからすべての要素この動作を変更するには、簡単な動きxsl:copy指示内部

    AとB.私たちは両方のファイルに対応する要素(同じ名前)を見つけることができますxsl:when

  • +0

    私はあなたが極度に単純化しているかもしれない問題。 XML文書は、同じ名前を持ち、階層内の複数の場所に複数の要素を持つことができます。 –

    +0

    はい、わかっています。これはサンプルコードを意味します。著者は、彼の文脈で "対応する"ことが意味することができるより良い基準を私たちに提供しなかった。もちろん、ID属性は素晴らしいでしょう。または、要素の祖先を考慮に入れることもできます(ただしこれは推測しています)。とにかく、私は彼の出発点を与えたかったのです。しかし、あなたのヒントのおかげで、私は最初から明確にしておくべきでした。 – SomeStupid

    +0

    私は真にジェネリックな解決策は考えられません。 –

    0

    私はそこに役立つコメントと混乱を避けるために、個別に改善された回答を提供しようとしています。

    「一般的な」アプローチ(要素名に一致)に加えて、ここでは特定のテンプレートも使用します。

    <?xml version="1.0" encoding="UTF-8"?> 
    <metadata> 
        <Table> 
         <Name /> 
         <Description/> <!-- NEW: not unique element name --> 
         <Location/> 
         <Citation> 
          <Title /> 
          <Abstract /> 
          <Description/> 
         </Citation> 
         <metadataDate/> 
        </Table> 
    </metadata> 
    

    ファイルA:

    <?xml version="1.0" encoding="UTF-8"?> 
    <metadata> 
        <Table> 
         <Name>SCHAME.table_name</Name> 
         <Location>oracle:TNS_1</Location> 
         <Description>Table description A</Description> <!-- NEW --> 
         <Citation> 
          <Title>Title 1</Title> 
          <Description/> 
         </Citation> 
         <metadataDate>20170418</metadataDate> 
        </Table> 
    </metadata> 
    

    ファイルB:

    試験、Iは、テンプレートとAとBを変更し、表の内側に直接別のDescription要素を追加するため

    <?xml version="1.0"?> 
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
    
        <xsl:output method="xml" indent="yes" /> 
        <xsl:strip-space elements="*"/> 
    
        <!-- Parameters --> 
        <xsl:param name="aFile"  select="'a.xml'" /> 
        <xsl:param name="bFile"  select="'b.xml'" /> 
    
        <!-- Variables --> 
        <xsl:variable name="aDoc" select="document($aFile, .)"/> 
        <xsl:variable name="bDoc" select="document($bFile, .)"/> 
    
        <!-- Locate elements by name in both files --> 
        <xsl:key name="elementsByName" match="*" use="name()" /> 
    
        <!-- Root-Template --> 
        <xsl:template match="/"> 
         <xsl:comment> 
          <xsl:value-of select="concat('Merge of ', $aFile, ' with ', $bFile)" /> 
         </xsl:comment> 
    
         <xsl:apply-templates /> 
        </xsl:template> 
    
        <!-- Merge specific elements --> 
        <xsl:template match="metadata/Table/Description"> 
         <xsl:call-template name="mergeElement"> 
          <xsl:with-param name="aValue" select="$aDoc/metadata/Table/Description/text()" /> 
          <xsl:with-param name="bValue" select="$bDoc/metadata/Table/Description/text()" /> 
         </xsl:call-template> 
        </xsl:template> 
    
        <xsl:template match="metadata/Table/Citation/Description"> 
         <xsl:call-template name="mergeElement"> 
          <xsl:with-param name="aValue" select="$aDoc/metadata/Table/Citation/Description/text()" /> 
          <xsl:with-param name="bValue" select="$bDoc/metadata/Table/Citation/Description/text()" /> 
         </xsl:call-template> 
        </xsl:template> 
    
        <!-- Merge unique elements --> 
        <xsl:template match="*" priority="-10"> 
         <xsl:variable name="elemName" select="name()" /> 
    
         <xsl:call-template name="mergeElement"> 
          <xsl:with-param name="aValue" select="$aDoc/key('elementsByName', $elemName)/text()" /> 
          <xsl:with-param name="bValue" select="$bDoc/key('elementsByName', $elemName)/text()" /> 
         </xsl:call-template> 
        </xsl:template> 
    
        <!-- Use A or B --> 
        <xsl:template name="mergeElement"> 
         <xsl:param name="aValue" /> 
         <xsl:param name="bValue" /> 
    
         <xsl:copy> 
          <xsl:choose> 
           <xsl:when test="$aValue != ''"> 
            <xsl:value-of select="$aValue" /> 
           </xsl:when> 
    
           <xsl:when test="$bValue != ''"> 
            <xsl:value-of select="$bValue" /> 
           </xsl:when> 
          </xsl:choose> 
    
          <xsl:apply-templates select="*" /> 
         </xsl:copy> 
        </xsl:template> 
    
    </xsl:stylesheet> 
    

    <?xml version="1.0" encoding="UTF-8"?> 
    <metadata> 
        <Table> 
         <Description>Table description B</Description> <!-- NEW --> 
         <Citation> 
          <Title>Template Title</Title> 
          <Abstract>Template Abstract</Abstract> 
          <Description>Template Description</Description> 
         </Citation> 
         <MetadataDate>20160131</MetadataDate> 
        </Table> 
    </metadata> 
    

    Generate dファイルC:

    <?xml version="1.0" encoding="UTF-8"?> 
    <!--Merge of a.xml with b.xml--> 
    <metadata> 
        <Table> 
         <Name>SCHAME.table_name</Name> 
         <Description>Table description A</Description> <!-- NEW --> 
         <Location>oracle:TNS_1</Location> 
         <Citation> 
         <Title>Title 1</Title> 
         <Abstract>Template Abstract</Abstract> 
         <Description>Template Description</Description> 
         </Citation> 
         <metadataDate>20170418</metadataDate> 
        </Table> 
    </metadata> 
    

    これは一般的な解決策ではありません。 しかし、一意でない要素の数が総数に比べて少ない場合は、汎用テンプレートの恩恵を受けるでしょう。固有でない要素だけが個別に「スタイリング」されなければなりません。

    関連する問題