XQuery / BaseX - Sonucun derinliğini sınırla

Oct 04 2020

XPath veya XQuery kullanırken, sonucun derinliğini sınırlamanın bir yolu var mı?

XQuery 3.1 ve XSLT 2.0'ı destekleyen BaseX kullanıyorum.

Örneğin, bu girdi belgesi verildiğinde:

<country name="United States">
  <state name="California">
    <county name="Alameda" >
      <city name="Alameda" />
      <city name="Oakland" />
      <city name="Piedmont" />
    </county>
    <county name="Los Angeles">
      <city name="Los Angeles" />
      <city name="Malibu" />
      <city name="Burbank" />
    </county>
    <county name="Marin">
      <city name="Fairfax" />
      <city name="Larkspur" />
      <city name="Ross" />
    </county>
    <county name="Sacramento">
      <city name="Folsom" />
      <city name="Elk Grove" />
      <city name="Sacramento" />
    </county>
  </state>
</country>

Bu sorguyu yürütürsem /country/stateşu sonucu alırım:

<state name="California">
  <county name="Alameda">
    <city name="Alameda"/>
    <city name="Oakland"/>
    <city name="Piedmont"/>
  </county>
  <county name="Los Angeles">
    <city name="Los Angeles"/>
    <city name="Malibu"/>
    <city name="Burbank"/>
  </county>
  <county name="Marin">
    <city name="Fairfax"/>
    <city name="Larkspur"/>
    <city name="Ross"/>
  </county>
  <county name="Sacramento">
    <city name="Folsom"/>
    <city name="Elk Grove"/>
    <city name="Sacramento"/>
  </county>
</state>

Sonucun derinliğini sınırlamak istiyorum. İdeal olarak, bir XPath sorgusunu sabit kodlamak yerine derinliği belirlemenin bir yolu olurdu.

Örnek olarak, sonucu torunları dahil etmemekle birlikte sonuç düğümleri ve alt öğeleriyle sınırlamak istiyorum, bu nedenle sonuç şöyle olacaktır:

<state name="California">
  <county name="Alameda" />
  <county name="Los Angeles" />
  <county name="Marin" />
  <county name="Sacramento" />
</state>

Yanıtlar

5 zx485 Oct 04 2020 at 05:58

Kolay ve anlaşılır bir yol, XSLT-2.0'ı boş bir şablonla kullanmaktır <county>. <xsl:strip-space>Çocukların kullandığı olurdu alanı kaldırır.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:strip-space elements="*" />
 
  <!-- Identity template -->
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()" />
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="/">
      <xsl:apply-templates select="/country/state" />
  </xsl:template>
  
  <xsl:template match="county/*" />
  
</xsl:stylesheet>

Çıktı:

<?xml version="1.0" encoding="UTF-8"?>
<state name="California">
    <county name="Alameda"/>
    <county name="Los Angeles"/>
    <county name="Marin"/>
    <county name="Sacramento"/>
</state>

XQuery ile bir çözüm şöyle görünebilir:

for $st in doc("b.xml")/country/state return element { node-name($st) } { $st/@*, for $ct in $st/county return element { node-name($ct) } { $ct/@* }
  }

Çıktı aynı.

1 MichaelKay Oct 04 2020 at 18:14

Aslında sorgunuzun sonucu state, kaynak belgedeki düğüm olan tek bir düğümdür . Bazı yazılımlar daha sonra sorgunun sonuçlarını - yani statedüğüm - belirli bir formatta görüntüler, ancak prensipte sonuçlar sorguyu değiştirmeden farklı bir şekilde görüntülenebilir. Örneğin, bu sorgunun sonuçlarını şu şekilde görüntüleyen bir yazılımın farkındayım:

/country[1]/state[1]

Öyleyse iki soruyu ayırmanız gerekir: Sorgu hangi düğümleri döndürür ve nasıl görüntülenir? Bazı durumlarda, birinci adımın ilgilenilen düğümleri seçtiği ve ikinci adımın sonuçların sunumunu kontrol ettiği bir işleme hattı oluşturmak mantıklı olabilir.

Şahsen ben XSLT'de her zaman ikinci adımı yapardım, ancak bazı insanlar XQuery'yi tercih ediyor. İstediğini al.

1 MikeChristiansen Oct 06 2020 at 20:05

@ zx845'in gönderisi beni doğru yola götürdü. Nihai hedefim, bir "özet" ve gerekirse daha derin sonuçlar elde etmek için ihtiyacım olan meta verileri elde etmek amacıyla sonucun derinliğini sınırlamaktı.

BaseX, herhangi bir düğümün dahili düğüm kimliğini döndürecek bir "db: düğüm kimliği" işlevine sahiptir . Orada : Başka bir fonksiyon, "açık-id db" Belirtilen kimliğe sahip düğümünü döndürür.

Verilen girdiyi varsayalım:

<country name="United States">
  <state name="California">
    <county name="Alameda">
      <city name="Alameda"/>
      <city name="Oakland"/>
      <city name="Piedmont"/>
    </county>
    <county name="Los Angeles">
      <city name="Los Angeles"/>
      <city name="Malibu"/>
      <city name="Burbank"/>
    </county>
    <county name="Marin">
      <city name="Fairfax"/>
      <city name="Larkspur"/>
      <city name="Ross"/>
    </county>
    <county name="Sacramento">
      <city name="Folsom"/>
      <city name="Elk Grove"/>
      <city name="Sacramento"/>
    </county>
  </state>
  <state name="New York">
    <county name="Albany">
      <city name="Albany"/>
      <city name="Cohoes"/>
      <city name="Watervliet"/>
    </county>
    <county name="Erie">
      <city name="Buffalo"/>
      <city name="Lackawanna"/>
      <city name="Tonawanda"/>
    </county>
  </state>
</country>

Derinliği kontrol etmeme ve her düğüm için düğüm kimliğini döndürmeme izin veren bu işlevi tanımladım.

declare function local:abbreviated($input, $depth as xs:integer)
{
  if($depth = 0) then element node { db:node-id($input)
    }
  else
    element { node-name($input) } { attribute node-id { db:node-id($input)
      },
      $input/@*, $input/text(),
      for $child in $input/*
        return local:abbreviated($child, $depth - 1)
    }
};

Aşağıdakileri yürütürsem:

declare variable $input := /country/state; for $result in $input return local:abbreviated($result, 1)

Sonra şu sonucu alıyorum:

<state node-id="3" name="California">
  <node>5</node>
  <node>13</node>
  <node>21</node>
  <node>29</node>
</state>
<state node-id="37" name="New York">
  <node>39</node>
  <node>47</node>
</state>

Şimdi, sonuçları işlediğimde, kullanıcı bir stateöğe için daha fazla ayrıntı isterse , her bir 'düğüm' öğesini işleyebilir ve düğümün gerçek içeriğini elde etmek için bu sorguyu çalıştırabilirim.

local:abbreviated(db:open-id('states', 5), 2)

Sonuçlanan:

<county node-id="5" name="Alameda">
  <city node-id="7" name="Alameda"/>
  <city node-id="9" name="Oakland"/>
  <city node-id="11" name="Piedmont"/>
</county>