1

私はまた。新しい問題があります。

必要な要素のみに xml 構造を削除/縮小するのが好きです。

問題を説明するために、単純なランダム構造を構築しました。

<ROOT>
    <DATA>
        <ALLOC>
            <TYPE>Test</TYPE>
            <NAME>something text</NAME>
            <VALUE>4711</VALUE>
        </ALLOC>
        <ALLOC>
            <TYPE>Test</TYPE>
            <NAME>something text</NAME>
            <VALUE>4712</VALUE>
        </ALLOC>
        <ALLOC>
            <TYPE>Test</TYPE>
            <NAME>something text</NAME>
            <VALUE>4713</VALUE>
        </ALLOC>
    </DATA>
    <SOURCE>
        <CONNECTION>
            <TYPE>SQL</TYPE>
            <VALUE>jdbc</VALUE>
            <CSTRING>jdbc string</CSTRING>
        </CONNECTION>
        <CONNECTION>
            <TYPE>CSV</TYPE>
            <VALUE>CSV</VALUE>
            <CSTRING></CSTRING>
        </CONNECTION>
    </SOURCE>
</ROOT>

必要な要素は次のとおりです。

/ROOT[1]/DATA[1]/ALLOC[2]/VALUE[1]
/ROOT[1]/SOURCE[1]/CONNECTION[1]/CSTRING[1]

必要な要素ステートメントは、xmlassert.equal > xmldiff を使用して Java から取得されます。

ここで、必要な要素に xml 構造を削除する必要がありますが、要素の xml 構造 (xpath) は保持します。

望ましい出力は次のとおりです。

<ROOT>
    <DATA>
        <ALLOC>
            <VALUE>4712</VALUE>
        </ALLOC>
    </DATA>
    <SOURCE>
        <CONNECTION>
            <CSTRING>jdbc string</CSTRING>
        </CONNECTION>       
    </SOURCE>
</ROOT>

実際の構造は非常に大きく (印刷する場合は、A4 ページの 6 倍以上)、複雑で、複数のレベルがあります。要求された要素も動的です。

私は過去数時間、多くの fourms でスレッドを読み取り、多くの異なる xslt を試し、より多くのスレッドを読み取ることに費やしました。

どうやってやるの?

よろしくお願いします。

4

2 に答える 2

1

どうやってやるの?

これは、短くて単純な XSLT 1.0 の一般的なソリューションです。

<xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:param name="pExpressions">
      <e>/ROOT[1]/DATA[1]/ALLOC[2]/VALUE[1]</e>
      <e>/ROOT[1]/SOURCE[1]/CONNECTION[1]/CSTRING[1]</e>
    </xsl:param>        
    <xsl:variable name="vExpressions" 
                  select="document('')/*/xsl:param[@name='pExpressions']/*"/>

    <xsl:template match="*">
      <xsl:variable name="vPath">
        <xsl:apply-templates select="ancestor-or-self::*" mode="path"/>
      </xsl:variable>

      <xsl:copy-of select="self::*[$vExpressions[.=$vPath]]"/>

      <xsl:apply-templates select=
      "self::*[$vExpressions[not(.=$vPath) and starts-with(.,$vPath)]]" mode="process"/>
    </xsl:template>

    <xsl:template match="*" mode="path">
        <xsl:value-of select="concat('/',name())"/>
        <xsl:variable name="vnumPrecSiblings" select=
         "count(preceding-sibling::*[name()=name(current())])"/>
        <xsl:value-of select="concat('[', $vnumPrecSiblings +1, ']')"/>
     </xsl:template>

     <xsl:template match="*" mode="process">
       <xsl:copy>
         <xsl:apply-templates select="*"/>
       </xsl:copy>
     </xsl:template>
</xsl:stylesheet>

この変換が提供された XML ドキュメントに適用されると、次のようになります。

<ROOT>
    <DATA>
        <ALLOC>
            <TYPE>Test</TYPE>
            <NAME>something text</NAME>
            <VALUE>4711</VALUE>
        </ALLOC>
        <ALLOC>
            <TYPE>Test</TYPE>
            <NAME>something text</NAME>
            <VALUE>4712</VALUE>
        </ALLOC>
        <ALLOC>
            <TYPE>Test</TYPE>
            <NAME>something text</NAME>
            <VALUE>4713</VALUE>
        </ALLOC>
    </DATA>
    <SOURCE>
        <CONNECTION>
            <TYPE>SQL</TYPE>
            <VALUE>jdbc</VALUE>
            <CSTRING>jdbc string</CSTRING>
        </CONNECTION>
        <CONNECTION>
            <TYPE>CSV</TYPE>
            <VALUE>CSV</VALUE>
            <CSTRING></CSTRING>
        </CONNECTION>
    </SOURCE>
</ROOT>

必要な正しい結果が生成されます。

<ROOT>
   <DATA>
      <ALLOC>
         <VALUE>4712</VALUE>
      </ALLOC>
   </DATA>
   <SOURCE>
      <CONNECTION>
         <CSTRING>jdbc string</CSTRING>
      </CONNECTION>
   </SOURCE>
</ROOT>

説明:

XML ドキュメント内のすべての要素について、その XPath 式が (質問で指定されたスタイルで) 生成されます。この要素は次のとおりです。

  • その XPath 式がパラメーターとして渡された XPath 式のいずれかと等しい場合、完全にコピーされます。
  • XPath 式が、パラメーターとして渡された 1 つ以上の XPath 式の文字列プレフィックスである場合、浅いコピー
  • それ以外の場合は無視 (削除)

ソリューションの汎用性:

入力 XPath 式は<xsl:param>、変換の呼び出し時に として渡すことができます。または、URI がパラメーターとして変換に渡される XML ファイルに入れることもできます。

:

最後の数時間は、多くの fourms でスレッドを読み取り、多くの異なる xslt を試し、より多くのスレッドを読み取ることに費やしました。

すべてのタイプのノードに対して XPath 式を生成する、より複雑で洗練された方法については、この回答を参照してください。

于 2016-11-05T04:45:28.807 に答える
0

私が理解しているように、一連の XPath 式を受け取り、入力 XML を XPath 式とその祖先に一致する要素のみに縮小する XSLT が必要です。

どの XSLT バージョンを使用するか、またはどのプロセッサを使用するかについては何も示していないため、適切なコード例を提供することは困難です。代わりに、選択できると思われるいくつかのオプションの概要を説明します。

  1. XPath ステートメントを入力として使用して、@ michael.hor257k の回答のような XSLT を (XSLT を使用して?) 生成し、そのXSLT を入力で実行します。これはおそらく十分に拡張できますが、かなりの量の初期投資が必要であり、他のオプションよりも作成が複雑になります。
  2. xsl:key および key() 関数を使用して、保持する要素を定義します。すべての祖先を保持したいことを忘れないでください。
  3. 関数、パラメーター、または呼び出しテンプレートを使用して、調べている要素に、XPath リストまたはその祖先のいずれかに対応する XPath アドレスがあるかどうかを評価します。パラメータを使用して、処理時間を大幅に節約できます。
  4. saxon:parse() または環境で使用できる場合と使用できない場合があるその他のカスタム関数を含むもの。

TMTOWTDI。どちらの方法を選択する場合でも、おそらく XSLT 2 を使用して、XPath アドレスのリストを文字列のシーケンスとして扱うことができます。物事を単純化するために、すべての祖先 ("/ROOT[1]/DATA[1]/ALLOC[2]"になる)を含めるようにそのシーケンスを拡張することも必要になるでしょう。("/ROOT[1]/DATA[1]/ALLOC[2]", "/ROOT[1]/DATA[1]", "/ROOT[1]")

うんざりして、あなたに XSLT 2 の実装をしてもらいました。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:local="http://example.com/local"
  exclude-result-prefixes="xs local"
  version="2.0">

  <xsl:output indent="yes"/>

  <xsl:param name="XPath" select="('/ROOT[1]/DATA[1]/ALLOC[2]/VALUE[1]', '/ROOT[1]/SOURCE[1]/CONNECTION[1]/CSTRING[1]')" as="xs:string+"/>

  <xsl:variable name="XPe" as="xs:string+">
    <xsl:for-each select="$XPath">
      <xsl:sequence select="local:ancestorize(.)"/>
    </xsl:for-each>
  </xsl:variable>

  <xsl:variable name="XPd" as="xs:string+">
    <xsl:sequence select="distinct-values($XPe)"/>
  </xsl:variable>

  <xsl:template match="@*|*">
    <xsl:param name="parentXP" as="xs:string?"/>
    <xsl:variable name="selfXP" as="xs:string">
      <xsl:variable name="seq">
        <xsl:value-of select="$parentXP"/>
        <xsl:text>/</xsl:text>
        <xsl:if test=". is ../@*">
          <!-- this test is a bit untested: you may need a better test to tell if you're looking at an attribute; I leave it as an exercise for you! -->
          <xsl:text>@</xsl:text>
        </xsl:if>
        <!-- I'm assuming no namespaces: if you have namespaces you'll have to build in your prefix here -->
        <xsl:value-of select="local-name()"/>
        <xsl:text>[</xsl:text>
        <xsl:value-of select="1 + count(preceding-sibling::*[name() eq current()/name()])"/>
        <xsl:text>]</xsl:text>
      </xsl:variable>
      <xsl:value-of select="xs:string($seq)"/>
    </xsl:variable>
    <xsl:if test="$selfXP = $XPd">
      <xsl:copy>
        <xsl:apply-templates select="@* | node()">
          <xsl:with-param name="parentXP" select="$selfXP"/>
        </xsl:apply-templates>
      </xsl:copy>
    </xsl:if>
  </xsl:template>

  <xsl:template match="text()">
    <xsl:param name="parentXP"/>
    <xsl:if test="$parentXP = $XPd and normalize-space(.) ne ''">
      <xsl:copy/>
    </xsl:if>
  </xsl:template>

  <xsl:function name="local:ancestorize" as="xs:string+">
    <xsl:param name="XPath" as="xs:string"/>
    <xsl:sequence select="$XPath"/>
    <xsl:if test="count(tokenize($XPath, '/')) gt 1">
      <xsl:sequence select="local:ancestorize(string-join((tokenize($XPath, '/'))[not(position() eq last())], '/'))"/>
    </xsl:if>
  </xsl:function>

</xsl:stylesheet>
于 2016-11-03T22:39:14.530 に答える