3

非常に悪い XML から Start オブジェクトを構築する必要があります。あるケースのためにSAXパーサーを作ったのですが、面倒なのでXPathを試してみたいと思います。

次の XML があります。

<doc>
    <start/>
    <a/>
    <b/>
    <item/>
    <item/>
    <item/>

    <start/>
    <item/>
    <item/>
    <item/>

    <start/>
    <b/>
    <item/>
    <item/>
    <item/>

</doc>

ただし、このドキュメントがもっと好きです(私は持っていません):

<doc>
    <start>
        <a/>
        <b/>
        <item/>
        <item/>
        <item/>
    <start/>

    <start>
        <item/>
        <item/>
        <item/>
    <start/>

    <start>
        <b/>
        <item/>
        <item/>
       <item/>
    <start/>

</doc>

2番目の「開始」ノードオブジェクトがあるとします(最初のXMLの例から)。ここで、このノードの直後に「a」要素と「b」要素を取得したいと思います。ただし、「b」ノードに対してこのノードから相対クエリを作成すると (次の兄弟を使用)、3 番目の開始ノードの下にノードが取得されます。「このノードに続くノード X を見つけるが、ノード Y で停止する (null を返す)」ということはできますか?

「|」を使用できることはわかっています 複数のクエリを OR しますが、これは私が望むものではありません (ただし、私の問題も解決する可能性があります)。

ありがとうございました。

4

3 に答える 3

4

XSLT 1.0を使用する場合は、キーを使用して隣接する兄弟をグループ化することもxsl:keyできるため、XPath式が単純化 されます。

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

    <xsl:key name="k_adjChild"
        match="/*/*[not(self::start)]"
        use="generate-id(preceding-sibling::start[1])"
        />

    <xsl:template match="doc">
        <doc>
            <xsl:apply-templates select="start"/>
        </doc>
    </xsl:template>

    <xsl:template match="start">
        <xsl:copy>
            <xsl:copy-of select="key('k_adjChild', generate-id())" />
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>
于 2011-07-29T01:51:59.060 に答える
3

コンテキストが特定の<start>要素であると仮定すると、この XPath は現在の<start>と次の の間のすべてのノードを選択します<start>

following-sibling::node()[not(self::start)]
                         [generate-id(preceding-sibling::start[1]) 
                           = generate-id(current())]

<start>この XSLT は、要素ごとにコンテンツをグループ化するためにその XPath を適用します。

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

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

    <xsl:template match="doc">
        <xsl:copy>
            <xsl:apply-templates select="@*|start" />
        </xsl:copy>
    </xsl:template>

    <!--for each start element, copy it,
        apply templates for it's attributes(in case any exist)
        and for nodes() that are following-siblings
        who's first preceeding-sibling is this start element-->
    <xsl:template match="start">
        <xsl:copy>
            <xsl:apply-templates select="@*
                | following-sibling::node()[not(self::start)]
                    [generate-id(preceding-sibling::start[1]) 
                      = generate-id(current())]" />
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>
于 2011-07-29T00:17:16.197 に答える
1

入力 XML がファイル内にあると仮定すると、in.xmlこの XQuery スクリプトは目的の処理を実行します。

(:
  This library function can be found here:
  http://www.xqueryfunctions.com/xq/functx_index-of-node.html
:)
declare namespace functx = "http://www.functx.com"; 
declare function functx:index-of-node($nodes as node()* ,
$nodeToFind as node() )  as xs:integer* 
{     
  for $seq in (1 to count($nodes))
  return $seq[$nodes[$seq] is $nodeToFind]
};

(:
  Recursively calculate the start elements with the other elements between
  as childs.
  Take the first two indices of $positions and create a start element
  with the elements of $elements with positions between these two indices.
  Then remove the first index of $position and do the recursive call.
  Input:
    $positions: Sequence with start element indices (belongs to $elements)
    $elements: Element sequence
  Output:
    Sequence of start elements with child elements
:)
declare function local:partition($positions as xs:integer*, 
    $elements as element()*) as element()* 
{
  let $len := count($positions)
  return
    if($len gt 1)
    then (
      let $first := $positions[1]
      let $second := $positions[2]
      let $rest := subsequence($positions, 2)
      return
        ( element start
          {
            subsequence($elements, $first + 1, $second - $first - 1)
          },
          local:partition($rest, $elements)
        )
    ) 
    else if($len eq 1)
    then (
          element start
          {
            subsequence($elements, $positions[1] + 1)
          }
    )
    else () 
};

(: Input document :)
let $input-doc := doc('in.xml')

(: Sequence of all child elements of root element doc :)
let $childs := $input-doc/doc/node()[. instance of element()]

(: Sequence with the indices of the start elements in $childs :)
let $positions := for $s in $input-doc/doc/start 
                  return functx:index-of-node($childs, $s)

return 
  <doc>
  {
    local:partition($positions, $childs)
  }
  </doc>

出力は次のとおりです。

<doc>
  <start>
    <a/>
    <b/>
    <item/>
    <item/>
    <item/>
  </start>
  <start>
    <item/>
    <item/>
    <item/>
  </start>
  <start>
    <b/>
    <item/>
    <item/>
    <item/>
  </start>
</doc>

XQilla でテストしますが、他のすべての XQuery プロセッサは同じ結果を生成するはずです。

于 2011-07-28T23:00:56.997 に答える